Potoki danych działające z szybkością TPU: tf.data.Dataset i TFRecords

1. Omówienie

TPU są bardzo szybkie. Strumień danych treningowych musi na bieżąco odpowiadać szybkości trenowania. W tym module nauczysz się wczytywać dane z GCS za pomocą interfejsu tf.data.Dataset API, aby podawać je do TPU.

Ten moduł jest częścią 1 z serii „Keras na TPU”. Możesz je wykonać w podanej kolejności lub niezależnie od siebie.

ca8cc21f6838eccc.png

Czego się nauczysz

  • Aby załadować dane treningowe, użyj interfejsu tf.data.Dataset API
  • Aby efektywnie wczytywać dane treningowe z GCS, używaj formatu TFRecord

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ł wykorzystuje Google Collaboratory i nie wymaga żadnej konfiguracji. Colaboratory to internetowa platforma do obsługi notatników do celów edukacyjnych. Oferuje on bezpłatne szkolenie dotyczące procesora CPU, GPU i TPU.

688858c21e3beff2.png

Aby zapoznać się z Colaboratory, możesz otworzyć ten przykładowy notatnik i przejrzeć kilka komórek.

c3df49e90e5a654f.png Welcome to Colab.ipynb

Wybierz backend TPU

8832c6208c99687d.png

W menu Colab wybierz Środowisko wykonawcze > Zmień typ środowiska wykonawczego, a następnie wybierz TPU. W tym laboratorium kodu skorzystasz z wydajnego TPU (jednostki Tensor Processing Unit) z przyspieszeniem sprzętowym. Połączenie z runtime nastąpi automatycznie przy pierwszym uruchomieniu lub możesz użyć przycisku „Połącz” w prawym górnym rogu.

Wykonywanie notatnika

76d05caa8b4db6da.png

Uruchom po kolei komórki, klikając wybraną komórkę i naciskając klawisze Shift+ENTER. Aby uruchomić cały notatnik, możesz też kliknąć Ś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

Niektóre komórki będą zawierać 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. Aby zdefiniować funkcje, musisz uruchomić te komórki.

Uwierzytelnianie

cdd4b41413100543.png

Colab może uzyskać dostęp do Twoich prywatnych zasobników Google Cloud Storage, jeśli uwierzytnisz się za pomocą autoryzowanego konta. Powyższy fragment kodu aktywuje proces uwierzytelniania.

3. [INFO] Czym są jednostki Tensor Processing Unit (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. TPU łączy klasyczny procesor wektorowy z dedykowaną jednostką mnożenia macierzy i doskonale sprawdza się w przypadku zadań, w których dominuje mnożenie dużych macierzy, np. w sieciach neuronowych.

8eb3e718b8e2ed08.png

Ilustracja: gęsta warstwa sieci neuronowej jako mnożenie macierzy z ośmioma obrazami przetwarzanymi przez sieć neuronową jednocześnie. 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

Rdzeń TPU v2 składa się z jednostki mnożenia macierzy (MXU), która wykonuje mnożenie macierzy, oraz jednostki przetwarzania wektorów (VPU) do wykonywania wszystkich innych zadań, takich jak aktywacje, softmax itp. VPU obsługuje obliczenia float32 i int32. Z kolei moduł MXU działa 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 neuronowej jest zwykle odporne na szum spowodowany zmniejszoną precyzją liczby zmiennoprzecinkowej. W niektórych przypadkach szum może nawet pomóc optymalizatorowi w konwergencji. 16-bitowa precyzja liczby zmiennoprzecinkowej była tradycyjnie używana do przyspieszania obliczeń, ale formaty float16 i float32 mają bardzo różne zakresy. Zmniejszenie precyzji z float32 na float16 zwykle powoduje przepełnienie i niedopełnienie. Istnieją rozwiązania, ale aby je zastosować, zwykle trzeba wykonać dodatkowe czynności.

Dlatego w procesorach TPU wprowadziliśmy format bfloat16. Jest to skrócona wersja typu float32 z tymi samymi bitami wykładniczymi i zakresem co typ 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żenia macierzy w sprzęcie za pomocą tak zwanej „tablicy skurczowej”, w którym elementy danych przepływają przez szereg sprzętowych jednostek obliczeniowych. (W medycynie „skurcz” 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 (patrz ilustracja na początku 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 zaprogramowano ten iloczyn skalarny na „rdzeń” GPU, a następnie uruchamiał go równolegle na dowolnej liczbie dostępnych rdzeni, aby spróbować naraz obliczyć wszystkie wartości wynikowej macierzy. Jeśli wynikowa matryca ma rozmiar 128 x 128, wymaga to dostępności 128 x 128 = 16 tys. „rdzeni”, co zwykle jest niemożliwe. Największe procesory graficzne mają około 4000 rdzeni. Z drugiej strony TPU używa minimalnej ilości sprzętu dla jednostek obliczeniowych w MXU: tylko bfloat16 x bfloat16 => float32 mnożniki i akumulatory, nic więcej. Są one tak małe, że TPU może zaimplementować 16 tys. z nich w MXU 128 × 128 i przetworzyć to mnożenie macierzy jednym poleceniem.

f1b283fc45966717.gif

Ilustracja: tablica skurczowa MXU. Elementy obliczeniowe to zasobniki pomnożone. Wartości jednej macierzy są wczytywane do tablicy (czerwone kropki). Wartości z 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.

Dodatkowo, gdy iloczyny są obliczane w MXU, sumy pośrednie są po prostu przesyłane między sąsiednimi jednostkami obliczeniowymi. Nie muszą być przechowywane ani pobierane z pamięci ani nawet z 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

Jeśli wyślesz żądanie Cloud TPU v2 w Google Cloud Platform, otrzymasz maszynę wirtualną z płytką TPU dołączoną przez PCI. Karta TPU ma 4 podwójne rdzenie 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 wtedy zazwyczaj połączona przez sieć z maszyną wirtualną, która jej zażądała. Pełny obraz wygląda tak:

dfce5522ed644ece.png

Ilustracja: maszyna wirtualna z podłączonym do sieci akceleratorem „Cloud TPU” „Cloud TPU” składa się z maszyny wirtualnej z kartą TPU podłączoną do gniazda PCI z 4 podwójnymi rdzeniami 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 i racki TPU połączone za pomocą interfejsu HPC.

Podczas trenowania gradienty są wymieniane między rdzeniami TPU za pomocą algorytmu all-reduce (dobry opis algorytmu all-reduce tutaj). Wytrenowany model może wykorzystywać możliwości sprzętowe przez trenowanie na dużych wielkościach wsadu.

d97b9cc5d40fdb1d.gif

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

Oprogramowanie

Trenowanie na dużym wsadzie

Idealna wielkość partii dla TPU to 128 elementów danych na rdzeń TPU, ale sprzęt może już dobrze wykorzystać 8 elementów danych na rdzeń TPU. Pamiętaj, że jedna z Cloud TPU ma 8 rdzeni.

W tym laboratorium kodu będziemy używać 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

Więcej wskazówek dotyczących 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.

Na zapleczu: XLA

Programy Tensorflow definiują grafy obliczeniowe. TPU nie uruchamia bezpośrednio kodu w Pythonie, tylko uruchamia wykres obliczeniowy zdefiniowany przez program Tensorflow. W tle kompilator XLA (kompilator przyspieszonej algebry liniowej) przekształca graf Tensorflow z węzłami obliczeniowymi w kod maszynowy TPU. Kompilator ten wykonuje też wiele zaawansowanych optymalizacji kodu i układu pamięci. Kompilacja odbywa się automatycznie, gdy zadanie jest wysył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

Od wersji Tensorflow 2.1 TPU są obsługiwane przez interfejs Keras API. 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 (zadań na AI Platform, Colaboratory, Kubeflow, maszyn wirtualnych do deep learningu utworzonych za pomocą narzędzia „ctpu up”). Te systemy wiedzą, gdzie znajduje się ich TPU dzięki zmiennej środowiskowej TPU_NAME. Jeśli tworzysz TPU ręcznie, ustaw zmienną środowiskową TPU_NAME na maszynie wirtualnej, z której korzystasz, lub wywołaj TPUClusterResolver z jawnymi parametrami: TPUClusterResolver(tp_uname, zone, project)
  • Element TPUStrategy implementuje rozkład i algorytm synchronizacji gradientu „all-reduce”.
  • Strategia jest stosowana w ramach 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

  • Dane do modelu Tensorflow można wczytywać na wiele sposobów, ale w przypadku TPU wymagane jest użycie interfejsu tf.data.Dataset API.
  • TPU są bardzo szybkie, a pozyskiwanie danych często staje się wąskim gardłem działające na nich. W przewodniku po skuteczności TPU znajdziesz narzędzia do wykrywania wąskich gardeł danych oraz inne wskazówki dotyczące 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 treningowego, czyli przejść do przodu i do tyłu przez model. W swojej ścieżce przetwarzania danych możesz nadal używać wszystkich operacji Tensorflow, ponieważ będą one wykonywane na procesorze.
  • tf.py_func nie jest obsługiwany na TPU.

4. Wczytuję dane

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

Będziemy pracować nad zbiorem danych zawierającym zdjęcia kwiatów. Celem jest nauczenie się podziału kwiatów na 5 typó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, które pojawiają się w miejscach oznaczonych etykietą „WYMAGANE DZIAŁANIA”.

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

Informacje dodatkowe

Informacje o zbiorze danych „flowers”

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. Po załadowaniu danych do zbioru danych interfejs API udostępnia wszystkie podstawowe funkcje przydatne do danych do trenowania sieci neuronowej:

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 wydajności i sprawdzone metody dotyczące zbiorów danych znajdziesz w tym artykule. Dokumentacja referencyjna znajduje się tutaj.

tf.data.Dataset – podstawy

Dane zwykle występują w kilku plikach, tutaj w obrazach. 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.

Następnie „mapuj” funkcję na każdą nazwę pliku, która normalnie wczyta i zdekoduje plik w rzeczywistych danych w pamięci:

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)

Aby iterować po zbiorze danych:

for data in my_dataset:
  print(data)

Zbiory danych krotek

W przypadku uczenia nadzorowanego 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. W ten sposób uzyskasz zbiór danych z tuplami, które będą zwracane podczas iteracji. 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 nad tym zbiorem danych zobaczysz, że możesz wczytywać około 1–2 obrazów na sekundę. To jest zbyt wolne! Akceleratory sprzętowe, których użyjemy do treningu, mogą osiągnąć wielokrotnie wyższą szybkość. Przejdź do następnej sekcji, aby dowiedzieć się, jak nam to pozwoli.

Rozwiązanie

Oto notatnik z rozwiązaniem. Możesz z niego skorzystać, jeśli utkniesz.

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

Omówione zagadnienia

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 Zbiory danych z tuplami
  • 😀 powtarzanie się z wykorzystaniem zbiorów danych

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

5. Szybkie wczytywanie danych

Akceleratory sprzętowe Tensor Processing Unit (TPU), których użyjemy w tym module, są bardzo szybkie. 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 tysiącach osobnych plików nie jest optymalnym 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

Optymalny układ danych zapewniający optymalną przepustowość usługi GCS

Format pliku TFRecord

Preferowanym przez Tensorflow formatem plików do przechowywania danych jest format TFRecord oparty na protobuf. Mogą też działać inne formaty serializacji, ale możesz załadować zbiór danych z plików TFRecord bezpośrednio, piszą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 równolegle odczytywać N plików, ignorując kolejność danych na rzecz szybkości odczytu.

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 ułamki 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ą, w przypadku braku pola zwrócona zostanie wartość domyślna, a nie błąd.

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. Gratulacje!

Możesz teraz przesyłać dane do TPU. Przejdź do następnego modułu

TPU w praktyce

Układy TPU i GPU są dostępne w Cloud AI Platform:

Na koniec – chętnie poznamy Twoją opinię. Poinformuj nas, jeśli zauważysz w tym module coś nie tak lub jeśli uważasz, że można go ulepszyć. Opinie można przesyłać za pomocą zgłoszeń na GitHubie [link do opinii].

HR.png

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