Keras i nowoczesne konwety w TPU

1. Omówienie

W tym module nauczysz się od podstaw budować, trenować i dostrajać własne splotowe sieci neuronowe przy użyciu Keras i TensorFlow 2. Teraz można to zrobić w ciągu kilku minut dzięki mocy jednostek TPU. Poznasz też różne podejścia, od bardzo prostej metody uczenia maszynowego do współczesnych architektur konwolucyjnych, takich jak Squeezenet. Ten moduł zawiera teoretyczne wyjaśnienia sieci neuronowych i jest dobrym punktem wyjścia dla deweloperów, którzy chcą poznać deep learning.

Czytanie artykułów z zakresu deep learning może być trudne i skomplikowane. Przyjrzyjmy się współczesnym splotowym architekturze sieci neuronowych.

ca8cc21f6838eccc.png

Czego się nauczysz

  • Możliwość korzystania z jednostek przetwarzania danych Keras i Tensor (TPU) w celu szybszego tworzenia modeli niestandardowych.
  • Używanie interfejsu tf.data.Dataset API i formatu TFRecord w celu efektywnego wczytywania danych treningowych.
  • Aby oszukiwać 😈, należy korzystać z nauczania transferowego zamiast tworzyć własne modele.
  • Używanie sekwencyjnych i funkcjonalnych stylów modelu Keras.
  • Możliwość utworzenia własnego klasyfikatora Keras z warstwą softmax i stratą entropii krzyżowej.
  • Aby dostroić model przy użyciu odpowiednich warstw splotowych.
  • Aby poznać nowe pomysły na architekturę konwnetów, takie jak moduły, średnia globalna agregacja danych itp.
  • Aby zbudować prostą, nowoczesną konwnetę za pomocą architektury Squeezenet.

Prześlij opinię

Jeśli zauważysz, że w tym laboratorium z kodem coś jest nie tak, daj nam znać. Opinię można przesłać, korzystając z formularza dotyczącego problemów na GitHubie [link do przesyłania opinii].

2. Krótki przewodnik po Google Colaboratory

Ten moduł używa Google Collaboratory i nie wymaga konfiguracji. Możesz go uruchomić z Chromebooka. Aby zapoznać się z notatnikami Colab, otwórz plik poniżej i uruchom te komórki.

c3df49e90e5a654f.png Welcome to Colab.ipynb

Wybierz backend TPU

8832c6208c99687d.png

W menu Colab wybierz Środowisko wykonawcze > Zmień typ środowiska wykonawczego i wybierz TPU. W tym module z kodem wykorzystasz potężną jednostkę TPU (Tensor Processing Unit) wyposażoną w procesor akceleracji sprzętowej. Połączenie ze środowiskiem wykonawczym następuje automatycznie przy pierwszym uruchomieniu. Możesz też skorzystać z opcji „Połącz” w prawym górnym rogu.

Wykonywanie notatnika

76d05caa8b4db6da.png

Uruchomienie komórki po kolei przez kliknięcie komórki i naciśnięcie klawiszy Shift+ENTER. Możesz też uruchomić cały notatnik, wybierając Środowisko wykonawcze > Uruchom wszystko

Spis treści

429f106990037ec4.png

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

Ukryte komórki

edc3dba45d26f12a.png

W niektórych komórkach będzie widoczny tylko tytuł. Jest to funkcja notatnika specyficzna dla Colab. Można je dwukrotnie kliknąć, aby zobaczyć w nich kod, ale zwykle nie jest to zbyt interesujące. Zwykle są to funkcje wspomagające lub wizualizacyjne. Nadal musisz uruchomić te komórki, aby zdefiniować funkcje znajdujące się w nich.

Uwierzytelnianie

cdd4b41413100543.png

Colab może uzyskać dostęp do Twoich prywatnych zasobników Google Cloud Storage, o ile uwierzytelnisz się na autoryzowanym koncie. Powyższy fragment kodu aktywuje proces uwierzytelniania.

3. [INFO] Czym są jednostki przetwarzania Tensor (TPU)?

W skrócie

f88cf6facfc70166.png

Kod do trenowania modelu w TPU w Keras (i korzystania z GPU lub CPU, jeśli jednostka TPU jest niedostępna):

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=...)

Wykorzystamy dziś jednostki TPU do utworzenia i zoptymalizowania klasyfikatora kwiatów z szybkością interaktywną (minuty na bieg trenowania).

688858c21e3beff2.png

Dlaczego TPU?

Nowoczesne procesory graficzne są zorganizowane wokół programowalnych „rdzeni”, czyli bardzo elastycznej architektury, która umożliwia im wykonywanie różnych zadań, takich jak renderowanie 3D, deep learning, symulacje fizyczne itp. Z kolei jednostki TPU łączą klasyczny procesor wektorowy z osobną jednostką mnożenia macierzy i świetnie sprawdzają się w każdym zadaniu, w którym dominują duże wielokrotności, takich jak sieci neuronowe.

8eb3e718b8e2ed08.png

Ilustracja: gęsta warstwa sieci neuronowej jako efekt mnożenia macierzy, z grupą 8 obrazów przetwarzanych jednocześnie przez sieć neuronową. Sprawdź, czy mnożenie 1 wiersza z kolumną ma ważoną sumę wszystkich wartości pikseli na obrazie. Warstwy splotowe można również przedstawić jako mnożniki macierzy, chociaż jest to nieco bardziej skomplikowane ( wyjaśnienie znajduje się w sekcji 1).

Sprzęt

MXU i VPU

Rdzenie TPU v2 składa się z mnożnej jednostki macierzy (MXU), która uruchamia mnożniki macierzy, oraz jednostki przetwarzania wektorów (VPU) do wszystkich innych zadań, takich jak aktywacje czy softmax. VPU obsługuje obliczenia float32 i int32. MXU działa natomiast w mieszanym, 16-32-bitowym formacie zmiennoprzecinkowym o mieszanej dokładności.

7d68944718f76b18.png

Mieszana precyzja zmiennoprzecinkowa i bfloat16

MXU oblicza mnożniki macierzy, używając danych wejściowych bfloat16 i danych wyjściowych float32. Zbiorcze gromadzenie danych jest wykonywane z dokładnością do float32.

19c5fc432840c714.png

Trenowanie sieci neuronowych jest zwykle odporne na szum generowany przez zmniejszoną precyzję liczby zmiennoprzecinkowej. W niektórych przypadkach szum pomaga nawet uzyskać zgodność optymalizatora. 16-bitowa precyzja zmiennoprzecinkowa jest tradycyjnie używana do przyspieszania obliczeń, ale formaty float16 i float32 mają bardzo różne zakresy. Zmniejszenie precyzji z float32 do float16 zwykle powoduje nadmierne i niedostateczne przepływy. Rozwiązania istnieją, ale do poprawnego działania typu float16 zazwyczaj trzeba się przyłożyć.

W związku z tym firma Google wprowadziła w przypadku jednostek TPU format bfloat16. Argument bfloat16 to skrócona liczba zmiennoprzecinkowa float32 z dokładnie takimi samymi liczbami bitów wykładnika i zakresem jak float32 W związku z tym, że jednostki TPU mnożą macierz obliczeniową z mieszaną precyzją z danymi wejściowymi bfloat16, ale z danymi wyjściowymi float32, oznacza to, że zwykle nie trzeba wprowadzać żadnych zmian w kodzie, aby skorzystać ze zmniejszonej dokładności, aby uzyskać wzrost wydajności.

Tablica skurczowa

MXU implementuje mnożniki macierzy w sprzęcie za pomocą tak zwanej „tablicy skurczowej” Architektura, w której elementy danych przepływają przez tablicę sprzętowych jednostek obliczeniowych. (W medycynie termin „skurczowy” odnosi się do skurczów serca i przepływu krwi, a tutaj mówimy o przepływie danych.

Podstawowym elementem mnożenia macierzy jest iloczyn skalarny między linią z jednej macierzy a kolumną z drugiej macierzy (zobacz ilustrację u góry tej sekcji). W przypadku mnożenia macierzy Y=X*W jednym z elementów wyniku będzie:

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]

W przypadku GPU można zaprogramować iloczyn skalarny na „rdzeń” GPU a następnie uruchamiaj go na wszystkich „rdzeniach” które są dostępne równolegle, aby spróbować wyliczyć wszystkie wartości wynikowej macierzy jednocześnie. Jeśli wynikowa macierz ma dużą wielkość 128 x 128, to wymagałoby to 128 x 128=16 tys. „rdzeni” co zwykle nie jest możliwe. Największe procesory graficzne mają około 4000 rdzeni. Z kolei TPU używa absolutnego minimum potrzebnego do obsługi jednostek obliczeniowych w MXU: mnożniki (bfloat16 x bfloat16 => float32) i nic więcej. Są one tak małe, że TPU może umieścić ich 16 KB w rozmiarze 128 x 128 MXU i przetworzyć to mnożenie macierzy za jednym razem.

f1b283fc45966717.gif

Ilustracja: tablica skurczowa MXU. Elementy obliczeniowe to zasobniki pomnożone. Wartości jednej macierzy są wczytywane do tablicy (czerwone kropki). Wartości innej macierzy przepływają przez tablicę (szare kropki). Linie pionowe propagują wartości w górę. Linie poziome przekazują częściowe sumy. W ramach ćwiczenia dla użytkownika należy sprawdzić, czy w miarę przepływu danych przez tablicę pojawia się wynik mnożenia macierzy po prawej stronie.

Poza tym, chociaż iloczyn skalarny są obliczane na MXU, sumy pośrednie przepływają po prostu między sąsiednimi jednostkami obliczeniowymi. Nie trzeba ich przechowywać ani pobierać do/z pamięci czy nawet do pliku rejestru. W efekcie architektura układów skurczowych TPU charakteryzuje się znaczną gęstością i mocą, a przy mnożeniu macierzy ma istotny wzrost szybkości w porównaniu z GPU.

Cloud TPU

Gdy poprosisz o „ Cloud TPU v2” w Google Cloud Platform otrzymujesz maszynę wirtualną z płytką TPU dołączoną do PCI. Płyta TPU jest wyposażona w 4 dwurdzeniowe układy TPU. Każdy rdzeń TPU obejmuje jednostkę VPU (Vector Processing Unit) i jednostkę mnożenia matriX o wymiarach 128 x 128 MXU. Ta „Cloud TPU” jest zwykle połączony przez sieć z maszyną wirtualną, która go zażądała. Pełny obraz wygląda więc tak:

dfce5522ed644ece.png

Ilustracja: maszyna wirtualna z podłączonym siecią „Cloud TPU” akceleratora. „Cloud TPU” jest wykonany z maszyny wirtualnej z płytką TPU dołączoną do PCI i 4 dwurdzeniowymi układami TPU.

Pody TPU

W centrach danych Google jednostki TPU są połączone z połączeniami międzysieciowymi o wysokiej wydajności (HPC), co może sprawić, że będą one postrzegane jako jeden bardzo duży akcelerator. Google nazywa je podami i mogą obejmować do 512 rdzeni TPU v2 lub 2048 rdzeni TPU v3.

2ec1e0d341e7fc34.jpeg

Ilustracja: pod TPU v3. Płyty TPU i szafy rackowe połączone za pomocą połączenia międzysieciowego HPC.

Podczas trenowania gradienty są wymieniane między rdzeniami TPU przy użyciu algorytmu all-reduce ( dobre wyjaśnienie funkcji all-reduce znajdziesz tutaj). Wytrenowany model może wykorzystać możliwości sprzętowe, trenując na dużych wsadach.

d97b9cc5d40fdb1d.gif

Ilustracja: synchronizacja gradientów podczas trenowania z wykorzystaniem algorytmu all-reduce w Google TPU w sieci toroidalnej siatki HPC 2-D.

Oprogramowanie

Trenowanie na dużym wsadzie

Idealny rozmiar wsadu dla jednostek TPU to 128 elementów danych na rdzeń TPU, ale sprzęt może już wykazywać dobre wykorzystanie z 8 elementów danych na rdzeń TPU. Pamiętaj, że jedna z Cloud TPU ma 8 rdzeni.

W tym module kodu użyjemy interfejsu Keras API. W Keras określona wsad jest reprezentowana przez globalny rozmiar wsadu dla całej jednostki TPU. Wsady zostaną automatycznie podzielone na 8 i będą uruchamiane w 8 rdzeniach TPU.

da534407825f01e3.png

Dodatkowe wskazówki dotyczące wydajności znajdziesz w przewodniku po wydajności TPU. W przypadku niektórych modeli należy zachować szczególną ostrożność w przypadku bardzo dużych wsadów. Więcej informacji znajdziesz w artykule LARSOptimizer.

Zaawansowane: XLA

Programy Tensorflow definiują grafy obliczeniowe. TPU nie uruchamia bezpośrednio kodu Pythona, tylko uruchamia wykres obliczeniowy zdefiniowany przez program Tensorflow. Działa w nim kompilator o nazwie XLA (przyspieszony kompilator linearnych algebra), który przekształca graf Tensorflow węzłów obliczeniowych w kod maszyny TPU. Ten kompilator przeprowadza też wiele zaawansowanych optymalizacji kodu i układu pamięci. Kompilacja odbywa się automatycznie, gdy dane są przesyłane do TPU. Nie musisz dodawać XLA do łańcucha tworzenia.

edce61112cd57972.png

Ilustracja: aby działał w TPU, graf obliczeniowy zdefiniowany w programie Tensorflow jest najpierw przekształcany w reprezentację formatu XLA (przyspieszonego kompilatora algebry liniowej), a następnie skompilowany przez XLA do kodu maszynowego TPU.

Używanie TPU w Keras

Jednostki TPU są obsługiwane przez interfejs Keras API od Tensorflow 2.1. Obsługa Keras działa na TPU i podach TPU. Ten przykład działa z użyciem 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 jednostkę 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”). Dzięki zmiennej środowiskowej TPU_NAME te systemy wiedzą, gdzie znajduje się ich TPU. Jeśli tworzysz TPU ręcznie, ustaw środ. TPU_NAME. zmien. na maszynie wirtualnej, z której go używasz, lub wywołaj funkcję TPUClusterResolver z jawnymi parametrami: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy to część, która implementuje dystrybucję oraz metodę „all-reduce” algorytm synchronizacji gradientu.
  • Strategia jest stosowana za pomocą zakresu. Model musi być zdefiniowany w zakresie strategii (zakres).
  • Funkcja tpu_model.fit oczekuje obiektu tf.data.Dataset na potrzeby danych wejściowych na potrzeby trenowania TPU.

Typowe zadania przenoszenia TPU

  • Istnieje wiele sposobów wczytywania danych w modelu Tensorflow, ale w przypadku jednostek TPU wymagany jest interfejs API tf.data.Dataset.
  • TPU są bardzo szybkie, a przetwarzanie danych często staje się wąskim gardłem działające na nich. W przewodniku po wydajności TPU znajdziesz narzędzia, które pomogą Ci wykrywać wąskie gardła danych i korzystać z innych wskazówek dotyczących wydajności.
  • Liczby int8 i int16 są traktowane jako int32. TPU nie ma sprzętu z liczbami całkowitymi działającymi na mniej niż 32 bitach.
  • Niektóre operacje Tensorflow nie są obsługiwane. Listę znajdziesz tutaj. Dobra wiadomość jest taka, że to ograniczenie dotyczy tylko kodu trenowania, tj. przekazywania przez model do przodu i do tyłu. Nadal możesz używać wszystkich operacji Tensorflow w potoku danych wejściowych, ponieważ będą one wykonywane na CPU.
  • Model tf.py_func nie jest obsługiwany przez TPU.

4. Wczytuję dane

c0ecb860e4cad0a9.jpeg cc4781a7739c49ae.jpeg 81236b00f8bbf39e.jpeg 961e2228974076bb.jpeg 7517dc163bdffcd5.jpeg 96392df4767f566d.png

Pracujemy ze zbiorem zdjęć kwiatów. Chcemy nauczyć się je kategoryzować na 5 rodzajów kwiatów. Dane są wczytywane za pomocą interfejsu API tf.data.Dataset. Najpierw poznajmy interfejs API.

Nauka praktyczna

Otwórz ten notatnik, uruchom komórki (Shift + ENTER) i postępuj zgodnie z instrukcjami w miejscu, w którym pojawia się komunikat „WYMAGANE PRACY”. .

c3df49e90e5a654f.png Fun with tf.data.Dataset (playground).ipynb

Informacje dodatkowe

Informacje o „kwiatach” zbiór danych

Zbiór danych jest podzielony na 5 folderów. Każdy folder zawiera kwiaty jednego rodzaju. Foldery te mają nazwy: słoneczniki, stokrotka, mniszek, tulipany i róże. Dane są przechowywane w publicznym zasobniku w Google Cloud Storage. Fragment:

gs://flowers-public/sunflowers/5139971615_434ff8ed8b_n.jpg
gs://flowers-public/daisy/8094774544_35465c1c64.jpg
gs://flowers-public/sunflowers/9309473873_9d62b9082e.jpg
gs://flowers-public/dandelion/19551343954_83bb52f310_m.jpg
gs://flowers-public/dandelion/14199664556_188b37e51e.jpg
gs://flowers-public/tulips/4290566894_c7f061583d_m.jpg
gs://flowers-public/roses/3065719996_c16ecd5551.jpg
gs://flowers-public/dandelion/8168031302_6e36f39d87.jpg
gs://flowers-public/sunflowers/9564240106_0577e919da_n.jpg
gs://flowers-public/daisy/14167543177_cd36b54ac6_n.jpg

Dlaczego tf.data.Dataset?

Keras i Tensorflow akceptują zbiory danych we wszystkich funkcjach trenowania i oceny. Gdy wczytasz dane do zbioru danych, interfejs API udostępnia wszystkie typowe funkcje przydatne w przypadku danych do trenowania sieci neuronowych:

dataset = ... # load something (see below)
dataset = dataset.shuffle(1000) # shuffle the dataset with a buffer of 1000
dataset = dataset.cache() # cache the dataset in RAM or on disk
dataset = dataset.repeat() # repeat the dataset indefinitely
dataset = dataset.batch(128) # batch data elements together in batches of 128
AUTOTUNE = tf.data.AUTOTUNE
dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch(es) while training

Wskazówki dotyczące skuteczności i sprawdzone metody dotyczące zbiorów danych znajdziesz w tym artykule. Dokumentacja referencyjna znajduje się tutaj.

Podstawowe informacje o zbiorze danych tf.data.Dataset

Dane zwykle są zapisane w wielu plikach, tutaj są obrazy. Zbiór danych z nazwami plików możesz utworzyć, wywołując:

filenames_dataset = tf.data.Dataset.list_files('gs://flowers-public/*/*.jpg')
# The parameter is a "glob" pattern that supports the * and ? wildcards.

Kliknij „Mapuj”, funkcję do każdej nazwy pliku, która zwykle wczytuje i dekoduje plik w pamięci rzeczywiste dane:

def decode_jpeg(filename):
  bits = tf.io.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  return image

image_dataset = filenames_dataset.map(decode_jpeg)
# this is now a dataset of decoded images (uint8 RGB format)

Powtarzanie zbioru danych:

for data in my_dataset:
  print(data)

Zbiory danych krotek

W uczeniu nadzorowanym zbiór danych treningowych składa się zwykle z par danych treningowych i prawidłowych odpowiedzi. Aby to umożliwić, funkcja dekodowania może zwracać krotki. Po wykonaniu iteracji otrzymasz zbiór danych z krotkami i krotkami, który zostanie zwrócony. Zwracane wartości to tensory Tensorflow gotowe do wykorzystania przez model. Możesz wywołać dla nich metodę .numpy(), aby wyświetlić nieprzetworzone wartości:

def decode_jpeg_and_label(filename):
  bits = tf.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  label = ... # extract flower name from folder name
  return image, label

image_dataset = filenames_dataset.map(decode_jpeg_and_label)
# this is now a dataset of (image, label) pairs 

for image, label in dataset:
  print(image.numpy().shape, label.numpy())

Wniosek:ładowanie pojedynczych obrazów jest powolne.

Podczas iteracji tego zbioru możesz zauważyć, że ładuje się np. 1–2 obrazy na sekundę. To jest zbyt wolne! Akceleratory sprzętowe, których będziemy używać do trenowania, w wielu przypadkach wytrzymają ten wskaźnik. W następnej sekcji dowiesz się, jak to zrobić.

Rozwiązanie

Oto notatnik rozwiązań. Możesz go użyć, jeśli napotkasz problemy.

c3df49e90e5a654f.png Fun with tf.data.Dataset (solution).ipynb

Omówione zagadnienia

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 Zbiory danych z krotkami
  • 😀 powtarzanie za pomocą zbiorów danych

Poświęć chwilę na przejrzenie tej listy kontrolnej.

5. Szybkie ładowanie danych

Akceleratory sprzętowe Tensor Processing Unit (TPU), których użyjemy w tym module, działają bardzo szybko. Często wyzwaniem jest jednak na tyle szybko, by dostarczyć im dane, by utrzymać ich zainteresowanie. Google Cloud Storage (GCS) jest w stanie utrzymać bardzo dużą przepustowość, ale tak jak w przypadku wszystkich systemów pamięci masowej w chmurze, inicjowanie połączenia z siecią wiąże się z pewnymi kosztami. Dlatego przechowywanie danych w postaci tysięcy pojedynczych plików nie jest idealnym rozwiązaniem. Podzielimy je na mniejszą liczbę plików i wykorzystamy możliwości zbioru tf.data.Dataset do równoległego odczytu z wielu plików.

Odczyt

Kod, który wczytuje pliki graficzne, zmienia ich rozmiar do wspólnego rozmiaru, a następnie zapisuje je w 16 plikach TFRecord, znajduje się w poniższym notatniku. Prosimy o szybkie zapoznanie się z nim. Jego wykonywanie nie jest konieczne, ponieważ w pozostałej części ćwiczenia z programowania otrzymasz dane w prawidłowym formacie TFRecord.

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

Idealny układ danych zapewniający optymalną przepustowość GCS

Format pliku TFRecord

Preferowanym przez Tensorflow formatem plików do przechowywania danych jest format TFRecord oparty na protobuf. Inne formaty serializacji też zadziałają, ale możesz załadować zbiór danych z plików TFRecord bezpośrednio, wpisując:

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

W celu uzyskania optymalnej wydajności zalecamy używanie poniższego bardziej złożonego kodu do odczytu z wielu plików TFRecord jednocześnie. Ten kod będzie odczytywać równolegle z N plików i zignorować kolejność danych na rzecz szybkości czytania.

AUTOTUNE = tf.data.AUTOTUNE
ignore_order = tf.data.Options()
ignore_order.experimental_deterministic = False

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)
dataset = dataset.with_options(ignore_order)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

Ściągawka TFRecord

W plikach TFRecord można przechowywać 3 typy danych: ciągi bajtów (lista bajtów), 64-bitowe liczby całkowite i 32-bitowe liczby zmiennoprzecinkowe. Są one zawsze przechowywane jako listy, a pojedynczy element danych będzie listą o rozmiarze 1. Aby zapisać dane w pliku TFRecord, możesz użyć poniższych funkcji pomocniczych.

zapis ciągów bajtów

# warning, the input is a list of byte strings, which are themselves lists of bytes
def _bytestring_feature(list_of_bytestrings):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=list_of_bytestrings))

zapisywanie liczb całkowitych

def _int_feature(list_of_ints): # int64
  return tf.train.Feature(int64_list=tf.train.Int64List(value=list_of_ints))

pisanie liczba zmiennoprzecinkowa

def _float_feature(list_of_floats): # float32
  return tf.train.Feature(float_list=tf.train.FloatList(value=list_of_floats))

napisanie TFRecord, korzystając z powyższych wskazówek

# input data in my_img_bytes, my_class, my_height, my_width, my_floats
with tf.python_io.TFRecordWriter(filename) as out_file:
  feature = {
    "image": _bytestring_feature([my_img_bytes]), # one image in the list
    "class": _int_feature([my_class]),            # one class in the list
    "size": _int_feature([my_height, my_width]),  # fixed length (2) list of ints
    "float_data": _float_feature(my_floats)       # variable length  list of floats
  }
  tf_record = tf.train.Example(features=tf.train.Features(feature=feature))
  out_file.write(tf_record.SerializeToString())

Aby odczytywać dane z rekordów TFRecord, musisz najpierw zadeklarować układ przechowywanych rekordów. W deklaracji możesz uzyskać dostęp do dowolnego nazwanego pola w postaci listy o stałej długości lub listy o zmiennej długości:

odczytywanie z plików TFRecord

def read_tfrecord(data):
  features = {
    # tf.string = byte string (not text string)
    "image": tf.io.FixedLenFeature([], tf.string), # shape [] means scalar, here, a single byte string
    "class": tf.io.FixedLenFeature([], tf.int64),  # shape [] means scalar, i.e. a single item
    "size": tf.io.FixedLenFeature([2], tf.int64),  # two integers
    "float_data": tf.io.VarLenFeature(tf.float32)  # a variable number of floats
  }

  # decode the TFRecord
  tf_record = tf.io.parse_single_example(data, features)

  # FixedLenFeature fields are now ready to use
  sz = tf_record['size']

  # Typical code for decoding compressed images
  image = tf.io.decode_jpeg(tf_record['image'], channels=3)

  # VarLenFeature fields require additional sparse.to_dense decoding
  float_data = tf.sparse.to_dense(tf_record['float_data'])

  return image, sz, float_data

# decoding a tf.data.TFRecordDataset
dataset = dataset.map(read_tfrecord)
# now a dataset of triplets (image, sz, float_data)

Przydatne fragmenty kodu:

odczytywanie pojedynczych elementów danych

tf.io.FixedLenFeature([], tf.string)   # for one byte string
tf.io.FixedLenFeature([], tf.int64)    # for one int
tf.io.FixedLenFeature([], tf.float32)  # for one float

czytanie list elementów o stałym rozmiarze

tf.io.FixedLenFeature([N], tf.string)   # list of N byte strings
tf.io.FixedLenFeature([N], tf.int64)    # list of N ints
tf.io.FixedLenFeature([N], tf.float32)  # list of N floats

odczytywanie zmiennej liczby elementów danych

tf.io.VarLenFeature(tf.string)   # list of byte strings
tf.io.VarLenFeature(tf.int64)    # list of ints
tf.io.VarLenFeature(tf.float32)  # list of floats

Funkcja VarLenFeature zwraca wektor rozproszony i po zdekodowaniu pliku TFRecord jest wymagany dodatkowy krok:

dense_data = tf.sparse.to_dense(tf_record['my_var_len_feature'])

W plikach TFRecord można też mieć opcjonalne pola. Jeśli podczas odczytu pola określisz wartość domyślną, a pole nie zostanie puste, to zamiast błędu będzie zwracana wartość domyślna.

tf.io.FixedLenFeature([], tf.int64, default_value=0) # this field is optional

Omówione zagadnienia

  • 🤔 fragmentacja plików danych z myślą o szybkim dostępie z GCS
  • 😓 jak zapisywać pliki TFRecord. (Nie pamiętasz już składni? Nie szkodzi. Dodaj tę stronę do zakładek jako ściągawkę).
  • 🤔 wczytuję zbiór danych z plików TFRecord przy użyciu TFRecordDataset

Poświęć chwilę na przejrzenie tej listy kontrolnej.

6. [INFO] Klasyfikator sieci neuronowych 101

W skrócie

Jeśli znasz już wszystkie terminy wyróżnione pogrubieniem w następnym akapicie, możesz przejść do następnego ćwiczenia. Jeśli dopiero zaczynasz przygodę z technologią deep learning, witamy w serwisie i czytaj dalej.

W przypadku modeli utworzonych jako sekwencja warstw Keras dostępny jest interfejs Sequential API. Na przykład klasyfikator obrazów wykorzystujący 3 gęste warstwy możesz zapisać w Keraście w taki 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, ... )

688858c21e3beff2.png

Gęsta sieć neuronowa

Jest to najprostsza sieć neuronowa do klasyfikowania obrazów. Składa się z „neuronów” ułożone w warstwy. Pierwsza warstwa przetwarza dane wejściowe, a potem wprowadza dane wyjściowe do innych warstw. Nazywamy to „gęstym” bo każdy neuron jest połączony ze wszystkimi neuronami w poprzedniej warstwie.

c21bae6dade487bc.png

Można przekazać obraz do takiej sieci, spłaszczając wartości RGB wszystkich jego pikseli do długiego wektora i używając go jako danych wejściowych. Nie jest to najlepsza metoda rozpoznawania obrazów, ale postaramy się ją ulepszyć.

Neurony, aktywacje, RELU

„Neuron” oblicza ważoną sumę wszystkich jego danych wejściowych i dodaje wartość zwaną „odchyleniem” i umożliwia stosowanie w niej tzw. „funkcji aktywacji”. Wagi i odchylenia są na początku nieznane. Zostaną zainicjowane losowo i nauczą się je. przez trenowanie sieci neuronowej na wielu znanych danych.

644f4213a4ee70e5.png

Najpopularniejszą funkcją aktywacji jest RELU (prostej jednostki liniowej). Jest to bardzo prosta funkcja, jak widać na wykresie powyżej.

Aktywacja Softmax

Powyższa sieć kończy się warstwą 5-neuronową, ponieważ kwiaty dzielimy na 5 kategorii (róża, tulipan, mniszek, stokrotka, słonecznik). Neurony w warstwach pośrednich są aktywowane przy użyciu klasycznej funkcji aktywacji RELU. Na ostatniej warstwie chcemy jednak obliczyć liczby z zakresu od 0 do 1, które reprezentują prawdopodobieństwo, że dany kwiat to róża, tulipan itd. W tym celu użyjemy funkcji aktywacji o nazwie „softmax”.

Zastosowanie funkcji softmax do wektora odbywa się przez zastosowanie wykładniczej każdego pierwiastka, a następnie normalizację wektora, zwykle z użyciem normy L1 (sumy wartości bezwzględnych), aby suma wartości wynosiła 1 i można ją było zinterpretować jako prawdopodobieństwo.

ef0d98c0952c262d.png d51252f75894479e.gif

Utrata entropii krzyżowej

Skoro nasza sieć neuronowa generuje prognozy na podstawie obrazów wejściowych, musimy sprawdzić, na ile są dobre, czyli odległość między informacjami z sieci a prawidłowymi odpowiedziami, często nazywanymi „etykietami”. Pamiętaj, że wszystkie obrazy w zbiorze danych mają prawidłowe etykiety.

Każda odległość byłaby odpowiednia, ale dla problemów klasyfikacyjnych można zastosować tak zwaną „dystans entropii krzyżowej” jest najskuteczniejszy. Nazywamy to błędem lub stratą. funkcja:

7bdf8753d20617fb.png

Efekt gradientu

„Szkolenie” sieć neuronowa faktycznie polega na używaniu obrazów i etykiet treningowych do korygowania wag i odchyleń w celu zminimalizowania funkcji utraty 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 obliczamy częściowe pochodne entropii krzyżowej względem wszystkich wag i wszystkich odchyleń, uzyskujemy „gradient” obliczony dla danego obrazu, etykiety oraz bieżącej wartości wag i odchylenia. Pamiętaj, że możemy mieć miliony wag i odchyleń, więc obliczenie gradientu wymaga sporo wysiłku. Na szczęście robi to za nas Tensorflow. Matematyczną właściwością gradientu jest to, że wskazuje on „do góry”. Chcemy dotrzeć tam, gdzie entropia krzyżowa jest niska, więc idziemy w przeciwną stronę. Wagi i odchylenia aktualizujemy według części gradientu. Następnie robimy to samo w pętli trenowania z kolejnymi partiami obrazów i etykiet do trenowania. Mamy nadzieję, że zjawisko to zbiega się w miejsce, w którym entropia krzyżowa jest minimalna, ale nic nie gwarantuje, że ta minimalna wartość będzie unikalna.

gradient 2.png

Krótkie partie i pęd

Możesz obliczyć gradient tylko na jednym przykładowym obrazie i od razu zaktualizować wagi i odchylenia, ale w przypadku grupy np. 128 obrazów uzyskasz gradient, który lepiej odpowiada ograniczeniom nałożonym przez różne przykładowe obrazy i z większym prawdopodobieństwem będzie szybciej zbliżyć się do rozwiązania. Wielkość minigrupy, którą można dostosować, jest parametrem.

Technika ta, czasem nazywana „stochastycznym gradientem”, ma jeszcze jedną, bardziej pragmatyczną zaletę: praca z wsadami oznacza również pracę z większymi macierzami, które zwykle łatwiej można zoptymalizować w przypadku układów GPU i TPU.

Zbieżność może być jednak nieco chaotyczna i może nawet zostać zatrzymana, jeśli wektor gradientu składa się wyłącznie ze zera. Czy to oznacza, że znaleźliśmy minimum? Nie zawsze. Minimalna lub maksymalna wartość komponentu gradientu może wynosić zero. W przypadku wektora gradientu zawierającego miliony elementów (jeśli wszystkie są zerami), prawdopodobieństwo, że każde zero odpowiada minimum i żadnym z nich nie znajdzie się w punkcie maksymalnym, jest bardzo małe. W przestrzeni obejmującej wiele wymiarów siedziska są dość powszechne i nie chcemy na nich poprzestać.

52e824fe4716c4a0.png

Ilustracja: siodło Gradient wynosi 0, ale nie jest on minimalny we wszystkich kierunkach. (Źródło grafiki: Wikimedia: By Nicoguaro – własne materiały, CC BY 3.0)

Rozwiązaniem jest dodanie pewnego pędu do algorytmu optymalizacji, tak aby mógł bez wahania pokonywać kolejne etapy.

Słownik

zbiorcze lub małowsadowe: trenowanie jest zawsze wykonywane na grupach danych i etykiet do trenowania. Ułatwia to stosowanie algorytmu. Grupa „zbiorcza” jest zwykle pierwszym wymiarem tensorów danych. Na przykład tensor kształtu [100, 192, 192, 3] zawiera 100 obrazów o wymiarach 192 x 192 piksele, z 3 wartościami na piksel (RGB).

Utrata entropii krzyżowej: specjalna funkcja straty często używana w klasyfikatorach.

gęsta warstwa: warstwa neuronów, w której każdy neuron jest połączony ze wszystkimi neuronami z poprzedniej warstwy.

funkcje: dane wejściowe sieci neuronowej są czasami nazywane „cechami”. Sztuka ustalania, które części zbioru danych (lub ich kombinacje) przekazać do sieci neuronowej w celu uzyskania dobrych prognoz, nosi nazwę „inżynierii cech”.

labels: inna nazwa „zajęć” lub popraw odpowiedzi w rozwiązywaniu zadań z klasyfikacją nadzorowaną

szybkość uczenia się: odsetek gradientu, według którego wagi i odchylenia są aktualizowane w każdej iteracji pętli trenowania.

logits: dane wyjściowe warstwy neuronów przed zastosowaniem funkcji aktywacji są nazywane „logitami”. Termin pochodzi z „funkcji logistycznej” inaczej „funkcja sigmoidalna” która była najpopularniejszą funkcją aktywacyjną. „Dane wyjściowe Neuron przed funkcją logistyki” została skrócona do „logits”.

strata: funkcja błędu porównująca dane wyjściowe sieci neuronowej z prawidłowymi odpowiedziami.

neuron: oblicza ważoną sumę swoich danych wejściowych, dodaje odchylenie i dostarcza wynik przy użyciu funkcji aktywacji.

kodowanie jednorazowe: klasa 3 z 5 jest zakodowana jako wektor pięciu elementów, z wyjątkiem trzeciego, czyli 1.

relu: wyprostowana jednostka liniowa. Popularna funkcja aktywacji neuronów.

sigmoid: inna funkcja aktywacji, która była kiedyś popularna i przydaje się w szczególnych przypadkach.

softmax: specjalna funkcja aktywacji, która działa na wektorze, zwiększa różnicę między największym składnikiem a pozostałymi, a także normalizuje wektor do sumy 1, co umożliwia zinterpretowanie go jako wektora prawdopodobieństw. Używany jako ostatni krok w klasyfikatorach.

tensor: „tensor”; jest jak macierz, ale z dowolną liczbą wymiarów. Jednowymiarowy tensor jest wektorem. Tensor dwuwymiarowy to macierz. Tensory mogą mieć 3, 4, 5 lub więcej wymiarów.

7. Przekazanie nauki

Gęste warstwy prawdopodobnie nie wystarczą do rozwiązania problemu z klasyfikacją obrazów. Musimy poznać warstwy splotowe i różne sposoby ich układania.

Ale możemy też pójść na skróty! Dostępne do pobrania są w pełni wytrenowane splotowe sieci neuronowe. Można odciąć ostatnią warstwę, czyli nagłówek klasyfikacji softmax, i zastąpić ją własną. Wszystkie wytrenowane wagi i odchylenia pozostają bez zmian. Ponownie trenujesz tylko dodaną warstwę softmax. Ta metoda nazywa się uczeniem transferowym i działa, o ile zbiór danych, na którym została wstępnie wytrenowana sieć neuronowa, jest „wystarczająco bliski”. do pana.

Ćwiczenie

Otwórz ten notatnik, uruchom komórki (Shift + ENTER) i postępuj zgodnie z instrukcjami w miejscu, w którym pojawia się komunikat „WYMAGANE PRACY”. .

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

Informacje dodatkowe

Dzięki uczeniu się w praktyce można korzystać zarówno z zaawansowanych splotowych architektur sieci neuronowych opracowanych przez najlepszych badaczy, jak i z wcześniejszego trenowania na ogromnym zbiorze obrazów. W naszym przypadku będziemy przenosić dane z sieci wytrenowanej w ImageNet – bazie zdjęć z wieloma roślinami i zdjęciami plenerowymi, która jest wystarczająco blisko kwiatów.

b8fc1efd2001f072.png

Ilustracja: wykorzystanie złożonej splotowej sieci neuronowej, która została już wytrenowana jako czarna ramka, ponownie trenuje tylko głowicę klasyfikacji. To jest nauczanie transferowe. Później zobaczymy, jak działają te skomplikowane układy splotowych warstw. Na razie ten problem ma ktoś inny.

Przenoszenie nauki w Keras

W Keras możesz utworzyć instancję wytrenowanego modelu z kolekcji tf.keras.applications.*. Na przykład MobileNet V2 to bardzo dobra architektura splotowa o doskonałej wielkości. Jeśli wybierzesz include_top=False, uzyskasz już wytrenowany model bez ostatniej warstwy softmax, dzięki czemu możesz dodać własny:

pretrained_model = tf.keras.applications.MobileNetV2(input_shape=[*IMAGE_SIZE, 3], include_top=False)
pretrained_model.trainable = False

model = tf.keras.Sequential([
    pretrained_model,
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(5, activation='softmax')
])

Zwróć też uwagę na ustawienie pretrained_model.trainable = False. Blokuje ciężary i odchylenia wytrenowanego modelu, dzięki czemu trenujesz tylko warstwę softmax. Zwykle wiąże się to ze stosunkowo niewielką wagą i może zostać przeprowadzone szybko i bez konieczności użycia bardzo dużego zbioru danych. Jeśli jednak masz dużo danych, pretrained_model.trainable = True może usprawnić naukę przenoszenia. Wstępnie wytrenowane wagi przyjmują świetne wartości początkowe, które można jeszcze dostosować przez trenowanie, aby lepiej dopasować je do problemu.

Na koniec zwróć uwagę na warstwę Flatten() włożoną przed gęstą warstwą softmax. Gęste warstwy działają na płaskich wektorach danych, ale nie wiemy, czy właśnie tak zwraca już wytrenowany model. Dlatego musimy to zrobić. W następnym rozdziale, przechodząc do architektur splotowych, wyjaśnimy format danych zwracany przez warstwy splotowe.

Przy takim podejściu możesz uzyskać dokładność na poziomie 75%.

Rozwiązanie

Oto notatnik rozwiązań. Możesz go użyć, jeśli napotkasz problemy.

c3df49e90e5a654f.png Keras Flowers transfer learning (solution).ipynb

Omówione zagadnienia

  • 🤔 Jak napisać klasyfikator w Keraście
  • 🤓 z ostatnią warstwą softmax i stratą entropii krzyżowej
  • 😈 Zmień naukę
  • 🤔 Wytrenuj swój pierwszy model
  • 🧐 Po straceniu i dokładności podczas treningu

Poświęć chwilę na przejrzenie tej listy kontrolnej.

8. [INFO] Konwolucyjne sieci neuronowe

W skrócie

Jeśli znasz już wszystkie terminy wyróżnione pogrubieniem w następnym akapicie, możesz przejść do następnego ćwiczenia. Jeśli dopiero zaczynasz korzystać ze splotowych sieci neuronowych, przeczytaj dalszą część artykułu.

convolutional.gif

Ilustracja: filtrowanie obrazu za pomocą 2 kolejnych filtrów o wymiarach 4 x 4 x 3=48 wag, które można wyuczyć.

Tak wygląda prosta splotowa sieć neuronowa w Keras:

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'])

688858c21e3beff2.png

Splotowe sieci neuronowe 101

W warstwie sieci splotowej jeden „neuron” wykonuje ważoną sumę pikseli tuż nad nim i tylko na niewielkim obszarze obrazu. Dodaje odchylenie i dostarcza sumę przez funkcję aktywacji, tak jak neuron w regularnej gęstej warstwie. Ta operacja jest następnie powtórzona na całym zdjęciu z tą samą wagą. Pamiętaj, że w gęstych warstwach każdy neuron ma własną wagę. Tutaj jest tylko jedna „poprawka” ciężarów przesuwają się po obrazie w obu kierunkach („splot”). Dane wyjściowe mają tyle wartości, ile jest pikseli na obrazie (wymagane jest jednak pewne dopełnienie na krawędziach). Jest to operacja filtrowania przy użyciu filtra wag 4 x 4=48.

Jednak 48 wag nie wystarczy. Aby dodać więcej stopni swobody, powtarzamy tę samą operację z nowym zestawem wag. Spowoduje to utworzenie nowego zestawu wyników filtra. Nazwijmy go „kanałem”. danych wyjściowych – analogia z kanałami R, G i B obrazu wejściowego.

Zrzut ekranu 2016-07-29 at 16.02.37.png

Dwa (lub więcej) zestawów wag można zsumować jako jeden tensor, dodając nowy wymiar. W ten sposób uzyskamy ogólny kształt tensora wag dla warstwy splotowej. Ponieważ liczba kanałów wejściowych i wyjściowych jest parametrami, możemy zacząć porządkować i łączyć splotowe warstwy.

d1b557707bcd1cb9.png

Ilustracja: splotowa sieć neuronowa przekształca „sześciany” danych na inne „sześciany”, danych.

Sploty kroczące, maksymalna liczba grupowania

Wykonując splot 2 lub 3, możemy również zmniejszyć wynikową kostkę danych w wymiarach poziomych. Można to zrobić na 2 sposoby:

  • Splot liniowy: filtr przesuwany jak powyżej, ale z krokiem >1
  • Maksymalne łączenie w ramach: przesuwane okno z operacją MAX (zwykle przy 2 × 2 poprawkach, powtarzane co 2 piksele).

2b2d4263bb8470b.gif

Ilustracja: przesunięcie okna przetwarzania o 3 piksele powoduje zmniejszenie wartości wyjściowych. Sploty rozciągnięte lub maksymalne złączenie (maks. przy przesuwaniu okna 2 x 2 o krok 2) to sposób zmniejszania kostki danych w wymiarach poziomych.

Klasyfikator synwolucyjny

Na koniec przypinamy głowę klasyfikacji, spłaszczając ostatnią kostkę danych i karmiąc ją przez gęstą, aktywowaną warstwę softmax. Typowy klasyfikator splotowy może wyglądać tak:

4a61aaffb6cba3d1

Ilustracja: klasyfikator obrazów wykorzystujący warstwy splotowe i softmax. Wykorzystuje filtry 3 x 3 i 1 x 1. Warstwy maksymalnej puli przyjmują maksymalną liczbę grup 2 x 2 punktów danych. Nagłówek klasyfikacji jest wdrożony z gęstą warstwą z aktywacją funkcji softmax.

W Keraście

Stos splotowy przedstawiony powyżej można zapisać w Keras w następujący 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'])

9. Konw. niestandardowa

Ćwiczenie

Zbudujemy i wytrenujemy splotową sieć neuronowa od zera. Użycie TPU pozwoli nam bardzo szybko wprowadzać poprawki. Otwórz ten notatnik, uruchom komórki (Shift + ENTER) i postępuj zgodnie z instrukcjami w miejscu, w którym pojawia się komunikat „WYMAGANE PRACY”. .

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

Celem jest przekroczenie 75% dokładności modelu uczenia się przenoszenia. Ten model ma tę zaletę, że został wstępnie wytrenowany na zbiorze danych składających się z milionów obrazów, podczas gdy mamy tutaj tylko 3670 obrazów. Czy uda Ci się chociaż je dopasować?

Informacje dodatkowe

Ile warstw i jaka jest ich rozmiar?

Wybór rozmiarów warstw to coś więcej niż sztuka. Trzeba znaleźć równowagę między zbyt małą liczbą parametrów a zbyt dużą liczbą parametrów (wag i uprzedzeń). Przy zbyt małej wadze sieć neuronowa nie jest w stanie reprezentować złożoności kształtów kwiatów. Zbyt duża ich liczba może powodować nadmierne dopasowanie, czyli brak specjalizacji w obrazach szkoleniowych i brak uogólnień. W przypadku dużej liczby parametrów trenowanie modelu będzie też powolne. W Keras funkcja model.summary() wyświetla strukturę i liczbę parametrów modelu:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 192, 192, 16)      448       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 192, 192, 30)      4350      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 96, 96, 60)        16260     
_________________________________________________________________
 ... 
_________________________________________________________________
global_average_pooling2d (Gl (None, 130)               0         
_________________________________________________________________
dense (Dense)                (None, 90)                11790     
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 455       
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________

Kilka wskazówek:

  • To właśnie dzięki temu jest „głębokim” i skuteczne sieci neuronowe. W przypadku tego prostego zadania rozpoznawania kwiatów sensowne jest od 5 do 10 warstw.
  • Używaj małych filtrów. Zwykle filtry 3 x 3 są dobre wszędzie.
  • Można też stosować tanie filtry 1 x 1. W rzeczywistości nie „filtrują”, nie tylko obliczać liniowe kombinacje kanałów. Zamiast nich użyj prawdziwych filtrów. (Więcej informacji o „splotach 1 x 1” znajdziesz w następnej sekcji).
  • W przypadku takiego problemu z klasyfikacją staraj się często przeprowadzać próbkowanie z użyciem maksymalnej liczby warstw puli (lub splotów z krokiem >1). Nie jest istotne, gdzie jest kwiat. Chodzi jedynie o to, że jest to róża czy mniszek, więc utrata informacji o wartościach x i y nie ma znaczenia, a filtrowanie mniejszych obszarów jest tańsze.
  • Liczba filtrów zwykle staje się podobna do liczby klas na końcu sieci (dlaczego? Zobacz poniżej trik „średnia globalna w pulacji”). Jeśli podzielisz się na setki klas, zwiększaj ich liczbę w kolejnych warstwach. W przypadku zbioru danych z kwiatami z 5 klasami filtrowanie za pomocą tylko 5 filtrów nie wystarczy. Możesz użyć tej samej liczby filtrów w większości warstw, np. 32, i zmniejszyć ją do końca.
  • Ostatnie gęste warstwy są drogie. Mogą mieć większą wagę niż wszystkie splotowe warstwy łącznie. Na przykład nawet przy bardzo uzasadnionych danych wyjściowych z ostatniej kostki 24 x 24 x 10 punktów danych, warstwa o gęstej gęstości 100 neuronów kosztowałaby 24 x 24 x 10 x 100=576 000 wag !!! Podejdź do tego tematu bardziej rozsądnie lub wypróbuj łączenie danych ze średniej wielkości globalnej (patrz poniżej).

Globalny pulowanie średniej

Zamiast korzystać z drogi, gęstej warstwy na końcu splotowej sieci neuronowej, możesz podzielić przychodzące dane „sześcianem” na tyle części, ile masz klas, uśredniać ich wartości i wprowadzać je za pomocą funkcji aktywacji softmax. Ten sposób tworzenia nagłówka klasyfikacji kosztuje 0 wag. W Keras składnia to tf.keras.layers.GlobalAveragePooling2D().

93240029f59df7c2.png

Rozwiązanie

Oto notatnik rozwiązań. Możesz go użyć, jeśli napotkasz problemy.

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

Omówione zagadnienia

  • 🤔 W tej grze z splotowymi warstwami
  • 🤓 Eksperymentujesz z maksymalną liczbą basenów, krokami, globalnymi wspólnymi pulami...
  • 😀 powtórzono szybkie rozwiązanie na rzeczywistym modelu z TPU

Poświęć chwilę na przejrzenie tej listy kontrolnej.

10. [INFO] Nowoczesna architektura splotowa

W skrócie

7968830b57b708c0.png

Ilustracja: splotowy „moduł”. Co jest teraz najlepsze? Warstwa z maksymalną pulą, po której następuje warstwa splotowa 1 x 1, czy inna kombinacja warstw? Wypróbuj je wszystkie, połącz wyniki i pozwól sieci zdecydować. Po prawej stronie: „ incepcja” splotowej architektury przy użyciu takich modułów.

Aby utworzyć w Keras modele, w których przepływ danych może się rozgałęziać i odchodzić, musisz użyć „funkcjonalnego” stylu modelu. 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)

688858c21e3beff2.png

Inne tanie sztuczki

Małe filtry 3 x 3

40a7b15fb7dbe75c.png

Na tej ilustracji widać wynik dwóch następujących po sobie filtrów 3 x 3. Spróbuj prześledzić, które punkty danych przyczyniły się do uzyskania wyniku: 2 kolejne filtry 3 × 3 obliczają kombinację regionu 5 × 5. Nie jest to taka sama kombinacja, jaką mógłby obliczyć filtr 5 x 5, ale warto spróbować, ponieważ dwa kolejne filtry 3 x 3 są tańsze niż pojedynczy filtr 5 x 5.

Sploty 1 x 1?

fd7cac16f8ecb423.png

W matematyce „1x1” to mnożenie przez stałą, niezbyt przydatne pojęcie. Należy jednak pamiętać, że w splotowych sieciach neuronowych filtr jest stosowany do kostki danych, a nie tylko obrazu 2D. Dlatego model „1x1”, oblicza ważoną sumę kolumny danych 1 x 1 (zobacz ilustrację). Przesuwając ją po danych, uzyskasz liniową kombinację kanałów danych wejściowych. To naprawdę przydatne. Jeśli wyobrazisz sobie kanały jako wyniki poszczególnych operacji filtrowania, na przykład filtr dla „spiczastych uszu”, inny dla „wąsów” a trzeci – „podkręcone oczy” a następnie „1x1” warstwa splotowa będzie obliczać wiele możliwych liniowych kombinacji tych cech, co może być przydatne w poszukiwaniu „kota”. Ponadto warstwy 1 x 1 mają mniejszy ciężar.

11. Kombinezon

Prosty sposób na połączenie tych pomysłów został omówiony w filmie „Squeezenet” papier. Autorzy sugerujemy bardzo prosty splotowy projekt modułów składający się wyłącznie z warstw splotowych 1 x 1 i 3 x 3.

1730ac375379269b.png

Ilustracja: architektura typu „squeezenet” oparta na „modułach pożarowych”. Naprzemienne są warstwy 1 x 1, które „ściskają” dane przychodzące w wymiarze pionowym, po których następują dwie równoległe warstwy 1x1 i 3x3 splotowe, które się rozwijają możliwość analizy głębi danych.

Ćwiczenie

Kontynuuj w poprzednim notatniku i stwórz splotową sieć neuronowa inspirowaną sweezenetem. Konieczna będzie zmiana kodu modelu na „styl funkcjonalny” Keras.

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

Dodatkowe informacje

W tym ćwiczeniu warto zdefiniować funkcję pomocniczą dla modułu ściągawki:

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)

Celem jest osiągnięcie 80% dokładności.

Do wypróbowania

Zacznij od jednej warstwy splotowej, a następnie do „fire_modules”, naprzemiennie z MaxPooling2D(pool_size=2) warstwy. Możesz eksperymentować z maksymalnie 2–4 warstwami pulsu w sieci oraz 1, 2 lub 3 kolejnymi modułami pożaru między maksymalną liczbą warstw połączonych w puli.

W modułach pożarów funkcja „ściskania” powinien być zwykle mniejszy od parametru „expand” . Parametry te są liczbami filtrów. Zazwyczaj może to być ich wartość od 8 do 196. Możesz poeksperymentować z architekturami, w których liczba filtrów stopniowo wzrasta w sieci, lub z prostymi architekturami, w których wszystkie moduły pożaru 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żna zauważyć, że eksperymenty nie idą zbyt dobrze, a cel dokładności na poziomie 80% wydaje się bardzo odległy. Czas na kilka tanich sztuczek.

Normalizacja wsadowa

Norma wsadowa pomoże rozwiązać napotkane problemy z konwersją. W jednym z kolejnych warsztatów objaśnimy tę technikę. Na razie potraktuj ją jak czarną skrzynkę. , dodając ten wiersz po każdej splotowej warstwie w Twojej sieci, w tym także 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 pędu należy zmniejszyć z wartości domyślnej od 0,99 do 0,9, ponieważ nasz zbiór danych jest mały. Na razie nie zapomnij o tych szczegółach.

Rozszerzanie danych

Możesz zyskać jeszcze kilka punktów procentowych, uzupełniając dane o łatwe przekształcenia, takie jak zmiany nasycenia w lewo:

4ed2958e09b487ca.png

ad795b70334e0d6b.png

Dzięki interfejsowi tf.data.Dataset API w TensorFlow jest to bardzo proste. 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 końcowym przekształceniu danych (komórka „zbiory danych do trenowania i walidacji”, 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 zaznaczyć opcji uzupełniania danych jako opcjonalnego i dodać niezbędny kod, aby rozszerzony był tylko zbiór danych do trenowania. Nie ma sensu rozszerzania zbioru danych do weryfikacji.

Dokładność na poziomie 80% w 35 epokach powinna być już w zasięgu.

Rozwiązanie

Oto notatnik rozwiązań. Możesz go użyć, jeśli napotkasz problemy.

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

Omówione zagadnienia

  • 🤔 Keras – „styl funkcjonalny” modele
  • 🤓 Architektura Squeezenet
  • 🤓 Rozszerzanie danych za pomocą tf.data.datset

Poświęć chwilę na przejrzenie tej listy kontrolnej.

12. Dostrojone urządzenie Xception

Rozdzielalne sploty

Ostatnio zyskuje na popularności inny sposób wdrażania warstw splotowych: głęboko rozdzielane sploty. Wiem, że to łyka, ale pomysł jest dość prosty. Są one zaimplementowane w TensorFlow i Kera jako tf.keras.layers.SeparableConv2D.

Rozdzielany splot również powoduje uruchomienie filtra na obrazie, ale dla każdego kanału obrazu wejściowego jest używany inny zestaw wag. Po nim następuje „splot 1 x 1”, czyli seria produktów kropkowych, której wynikiem jest ważona suma odfiltrowanych kanałów. Z każdą nową wagą obliczana jest tyle ważonych ponownych kombinacji kanałów, ile jest konieczne.

615720b803bf8dda.gif

Ilustracja: rozłączne sploty Faza 1. sploty z osobnym filtrem dla każdego kanału. Faza 2. Liniowe rekombinacje kanałów. Powtarzaj czynność z nowym zestawem wag, aż osiągniesz żądaną liczbę kanałów wyjściowych. Etap 1 można również powtórzyć z nową wagą, ale w praktyce rzadko się to udaje.

Rozdzielane sploty są stosowane w najnowszych architekturach sieci konwwolucyjnych: MobileNetV2, Xception i EfficientNet. Przy okazji – system MobileNetV2 był wcześniej używany do przenoszenia systemów uczących się.

Są tańsze niż zwykłe sploty i zostały uznane za równie skuteczne w praktyce. Oto waga dla przykładu zilustrowanego powyżej:

Warstwa splotowa: 4 x 4 x 3 x 5 = 240

Rozdzielana warstwa splotowa: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63

Czytelnik musi jedynie obliczyć liczbę mnożeń, która jest wymagana do zastosowania każdego ze splotowych warstw skal w podobny sposób. Rozdzielalne sploty są mniejsze i zapewniają znacznie większą wydajność obliczeniową.

Ćwiczenie

Zacznij od „transferu wiedzy” jako wytrenowany model, ale tym razem wybierz Xception. Xception używa tylko scalonych splotów. Pozostaw możliwość trenowania wszystkich ciężarów. Będziemy dostrajać już wytrenowane wagi na naszych danych, zamiast korzystać z wytrenowanych warstw.

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

Cel: dokładność > 95% (nie, poważnie, to możliwe!)

To ostatnie ćwiczenie, które wymaga trochę pracy nad kodem i badaniem danych.

Dodatkowe informacje o dostrajaniu

Usługa Xception jest dostępna w standardowych, wytrenowanych modelach w tf.keras.application.* Nie zapomnij tym razem wytrenować wszystkich ciężarów.

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żywać harmonogramu tempa uczenia się z okresem optymalizacyjnym. W ten sposób:

9b1af213b2b36d47.png

Rozpoczęcie od standardowego tempa uczenia się zakłóciłoby działanie wytrenowanych wag modelu. Zaczynając w stopniowy sposób zachowując je, dopóki model nie zacznie korzystać z danych, będzie w stanie je w rozsądny sposób modyfikować. Po wejściu możesz kontynuować naukę w stałym lub coraz malejącym tempie.

W Keras tempo uczenia się jest określane przez wywołanie zwrotne, w którym można obliczyć odpowiednie tempo uczenia się dla każdej epoki. Keras przekaże optymalizatorowi 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 rozwiązań. Możesz go użyć, jeśli napotkasz problemy.

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

Omówione zagadnienia

  • 🤔 Splot z możliwością rozdzielenia głębokości
  • 🤓 Harmonogramy tempa nauki
  • 😈 do dostrajania wytrenowanego modelu.

Poświęć chwilę na przejrzenie tej listy kontrolnej.

13. Gratulacje!

Udało Ci się zbudować pierwszą nowoczesną splotową sieć neuronową i wytrenować ją do ponad 90% dokładności, a dzięki TPU – kolejne iteracje w kolejnych trenowaniach zajmuje tylko kilka minut.

TPU w praktyce

TPU i GPU są dostępne w Vertex AI Google Cloud:

Bardzo zależy nam na opiniach użytkowników. Daj nam znać, jeśli zauważysz, że w tym module coś jest nie tak lub jeśli uważasz, że coś jest nie tak. Opinię można przesłać, korzystając z formularza dotyczącego problemów na GitHubie [link do przesyłania opinii].

HR.png

Martin Görner ID small.jpg
Autor: Martin Görner
Twitter: @martin_gorner