Datenpipelines mit TPU-Geschwindigkeit: tf.data.Dataset und TFRecords

1. Übersicht

TPUs sind sehr schnell. Der Stream der Trainingsdaten muss mit der Trainingsgeschwindigkeit mithalten. In diesem Lab lernen Sie, wie Sie Daten aus GCS mit der tf.data.Dataset API laden, um Ihre TPU zu speisen.

Dieses Lab ist Teil 1 der Reihe „Keras on TPU“. Sie können sie in der folgenden Reihenfolge oder unabhängig voneinander durchführen.

ca8cc21f6838eccc.png

Lerninhalte

  • Die tf.data.Dataset API zum Laden von Trainingsdaten verwenden
  • TFRecord-Format zum effizienten Laden von Trainingsdaten aus GCS verwenden

Feedback

Wenn Sie in diesem Codelab etwas Ungewöhnliches sehen, teilen Sie uns das bitte mit. Feedback kann über GitHub-Probleme [ Feedback-Link] gegeben werden.

2. Google Colaboratory – Kurzanleitung

In diesem Lab wird Google Colaboratory verwendet. Sie müssen nichts einrichten. Colaboratory ist eine Online-Notebook-Plattform für Bildungszwecke. Es bietet kostenloses CPU-, GPU- und TPU-Training.

688858c21e3beff2.png

Sie können dieses Beispiel-Notebook öffnen und einige Zellen durchlaufen, um sich mit Colaboratory vertraut zu machen.

c3df49e90e5a654f.png Welcome to Colab.ipynb

TPU-Backend auswählen

8832c6208c99687d.png

Wählen Sie im Colab-Menü Laufzeit > Laufzeittyp ändern und dann „TPU“ aus. In diesem Codelab verwenden Sie eine leistungsstarke TPU (Tensor Processing Unit) für hardwarebeschleunigtes Training. Die Verbindung zur Laufzeit erfolgt bei der ersten Ausführung automatisch. Sie können aber auch die Schaltfläche „Verbinden“ rechts oben verwenden.

Notebook-Ausführung

76d05caa8b4db6da.png

Führen Sie die Zellen einzeln aus, indem Sie auf eine Zelle klicken und UMSCHALTTASTE + EINGABETASTE drücken. Sie können das gesamte Notebook auch mit Laufzeit > Alle ausführen ausführen.

Inhaltsverzeichnis

429f106990037ec4.png

Alle Notebooks haben ein Inhaltsverzeichnis. Sie können sie über den schwarzen Pfeil auf der linken Seite öffnen.

Ausgeblendete Zellen

edc3dba45d26f12a.png

Bei einigen Zellen wird nur der Titel angezeigt. Dies ist eine Colab-spezifische Notebook-Funktion. Sie können doppelt darauf klicken, um den Code zu sehen, aber das ist in der Regel nicht sehr interessant. Sie unterstützen in der Regel Support- oder Visualisierungsfunktionen. Sie müssen diese Zellen weiterhin ausführen, damit die Funktionen darin definiert werden.

Authentifizierung

cdd4b41413100543.png

Colab kann auf Ihre privaten Google Cloud Storage-Buckets zugreifen, sofern Sie sich mit einem autorisierten Konto authentifizieren. Das obige Code-Snippet löst einen Authentifizierungsprozess aus.

3. [INFO] Was sind Tensor Processing Units (TPUs)?

Kurz zusammengefasst

f88cf6facfc70166.png

Der Code zum Trainieren eines Modells auf einer TPU in Keras (mit Fallback auf GPU oder CPU, wenn keine TPU verfügbar ist):

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

Wir verwenden heute TPUs, um einen Blumenklassifikator in interaktiver Geschwindigkeit (Minuten pro Trainingslauf) zu erstellen und zu optimieren.

688858c21e3beff2.png

Warum TPUs?

Moderne GPUs sind um programmierbare „Cores“ herum organisiert. Diese sehr flexible Architektur ermöglicht es ihnen, eine Vielzahl von Aufgaben wie 3D-Rendering, Deep Learning und physikalische Simulationen zu bewältigen. TPUs hingegen kombinieren einen klassischen Vektorprozessor mit einer dedizierten Matrixmultiplikationseinheit und eignen sich hervorragend für alle Aufgaben, bei denen große Matrixmultiplikationen dominieren, z. B. neuronale Netze.

8eb3e718b8e2ed08.png

Abbildung: Eine dichte neuronale Netzwerkebene als Matrixmultiplikation, bei der ein Batch von acht Bildern gleichzeitig durch das neuronale Netzwerk verarbeitet wird. Führen Sie eine Zeile-mal-Spalte-Multiplikation durch, um zu prüfen, ob tatsächlich eine gewichtete Summe aller Pixelwerte eines Bildes berechnet wird. Auch Faltungsebenen können als Matrixmultiplikationen dargestellt werden, obwohl das etwas komplizierter ist ( weitere Informationen in Abschnitt 1).

Die Hardware

MXU und VPU

Ein TPU v2-Kern besteht aus einer Matrix Multiply Unit (MXU), die Matrixmultiplikationen ausführt, und einer Vector Processing Unit (VPU) für alle anderen Aufgaben wie Aktivierungen, Softmax usw. Die VPU verarbeitet float32- und int32-Berechnungen. Die MXU hingegen arbeitet in einem gemischten 16-32-Bit-Gleitkommaformat.

7d68944718f76b18.png

Gleitkommawerte mit gemischter Precision und bfloat16

Die MXU berechnet Matrixmultiplikationen mit bfloat16-Eingaben und float32-Ausgaben. Zwischensummen werden mit float32-Genauigkeit berechnet.

19c5fc432840c714.png

Das Training neuronaler Netzwerke ist in der Regel unempfindlich gegenüber dem Rauschen, das durch eine reduzierte Gleitkommazahlgenauigkeit entsteht. In einigen Fällen kann Rauschen sogar dazu beitragen, dass der Optimierer konvergiert. Die 16‑Bit-Gleitkommazahl-Genauigkeit wurde traditionell verwendet, um Berechnungen zu beschleunigen. Die float16- und float32-Formate haben jedoch sehr unterschiedliche Bereiche. Wenn Sie die Genauigkeit von float32 auf float16 reduzieren, kommt es in der Regel zu Über- und Unterläufen. Es gibt Lösungen, aber in der Regel ist zusätzlicher Aufwand erforderlich, um float16 zu verwenden.

Aus diesem Grund hat Google das bfloat16-Format in TPUs eingeführt. bfloat16 ist ein abgeschnittenes float32 mit genau denselben Exponentenbits und demselben Bereich wie float32. Da TPUs Matrixmultiplikationen in gemischter Präzision mit bfloat16-Eingaben, aber float32-Ausgaben berechnen, sind in der Regel keine Codeänderungen erforderlich, um von den Leistungssteigerungen durch die reduzierte Präzision zu profitieren.

Systolic array (Systolisches Array)

Die MXU implementiert Matrixmultiplikationen in Hardware mithilfe einer sogenannten „systolischen Array“-Architektur, in der Daten durch ein Array von Hardware-Recheneinheiten fließen. In der Medizin bezieht sich „systolisch“ auf Herzkontraktionen und den Blutfluss, hier auf den Datenfluss.

Das grundlegende Element einer Matrixmultiplikation ist ein Skalarprodukt zwischen einer Zeile aus einer Matrix und einer Spalte aus der anderen Matrix (siehe Abbildung oben in diesem Abschnitt). Bei einer Matrixmultiplikation Y=X*W wäre ein Element des Ergebnisses:

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]

Auf einer GPU würde man dieses Skalarprodukt in einen GPU-„Kern“ programmieren und es dann auf so vielen „Kernen“ wie möglich parallel ausführen, um jeden Wert der resultierenden Matrix gleichzeitig zu berechnen. Wenn die resultierende Matrix 128 × 128 groß ist, wären 128 × 128=16.000 „Kerne“ erforderlich, was in der Regel nicht möglich ist. Die größten GPUs haben etwa 4.000 Kerne. Eine TPU hingegen verwendet das absolute Minimum an Hardware für die Recheneinheiten in der MXU: nur bfloat16 x bfloat16 => float32 Multiply-Accumulators, nichts anderes. Sie sind so klein, dass eine TPU 16.000 davon in einer 128 × 128 MXU implementieren und diese Matrixmultiplikation in einem Durchgang verarbeiten kann.

f1b283fc45966717.gif

Abbildung: Das systolische MXU-Array. Die Recheneinheiten sind Multiplikationsakkumulatoren. Die Werte einer Matrix werden in das Array geladen (rote Punkte). Die Werte der anderen Matrix fließen durch das Array (graue Punkte). Vertikale Linien übertragen die Werte nach oben. Horizontale Linien übertragen Teilergebnisse. Es bleibt dem Nutzer überlassen, zu überprüfen, ob das Ergebnis der Matrixmultiplikation auf der rechten Seite ausgegeben wird, wenn die Daten durch das Array fließen.

Außerdem werden die Zwischensummen, während die Punktprodukte in einer MXU berechnet werden, einfach zwischen benachbarten Recheneinheiten übertragen. Sie müssen nicht im Arbeitsspeicher oder in einer Registerdatei gespeichert und abgerufen werden. Das Ergebnis ist, dass die TPU-Architektur mit systolischen Arrays einen erheblichen Vorteil in Bezug auf Dichte und Stromverbrauch sowie einen nicht unerheblichen Geschwindigkeitsvorteil gegenüber einer GPU bei der Berechnung von Matrixmultiplikationen bietet.

Cloud TPU

Wenn Sie eine Cloud TPU v2 auf Google Cloud Platform anfordern, erhalten Sie eine virtuelle Maschine (VM) mit einer über PCI angeschlossenen TPU-Karte. Das TPU-Board hat vier Dual-Core-TPU-Chips. Jeder TPU-Kern verfügt über eine VPU (Vector Processing Unit) und eine 128 × 128 MXU (MatriX Multiply Unit). Diese „Cloud TPU“ wird dann in der Regel über das Netzwerk mit der VM verbunden, die sie angefordert hat. Das vollständige Bild sieht so aus:

dfce5522ed644ece.png

Abbildung: Ihre VM mit einem netzwerkgebundenen „Cloud TPU“-Beschleuniger. Die Cloud TPU selbst besteht aus einer VM mit einem PCI-angebundenen TPU-Board mit vier Dual-Core-TPU-Chips.

TPU-Pods

In den Rechenzentren von Google sind TPUs mit einem HPC-Interconnect (High Performance Computing, Hochleistungs-Computing) verbunden, sodass sie als ein sehr großer Beschleuniger erscheinen können. Google bezeichnet sie als Pods. Sie können bis zu 512 TPU v2-Kerne oder 2.048 TPU v3-Kerne umfassen.

2ec1e0d341e7fc34.jpeg

Abbildung: TPU v3-Pod. TPU-Boards und ‑Racks, die über HPC-Interconnect-Verbindungen verbunden sind.

Während des Trainings werden Gradienten zwischen TPU-Kernen mit dem All-Reduce-Algorithmus ausgetauscht ( gute Erklärung von All-Reduce). Das trainierte Modell kann die Hardware nutzen, indem es mit großen Batchgrößen trainiert wird.

d97b9cc5d40fdb1d.gif

Abbildung: Synchronisierung von Gradienten während des Trainings mit dem All-Reduce-Algorithmus im zweidimensionalen toroidförmigen HPC-Netzwerk von Google TPU.

Die Software

Training mit großer Batchgröße

Die ideale Batchgröße für TPUs beträgt 128 Datenelemente pro TPU-Kern. Die Hardware kann jedoch bereits ab 8 Datenelementen pro TPU-Kern gut genutzt werden. Eine Cloud TPU hat 8 Kerne.

In diesem Codelab verwenden wir die Keras API. In Keras ist der von Ihnen angegebene Batch die globale Batchgröße für die gesamte TPU. Ihre Batches werden automatisch in 8 Teile aufgeteilt und auf den 8 Kernen der TPU ausgeführt.

da534407825f01e3.png

Weitere Tipps zur Leistung finden Sie im Leistungsleitfaden für Cloud TPU. Bei sehr großen Batchgrößen ist bei einigen Modellen besondere Vorsicht geboten. Weitere Informationen finden Sie unter LARSOptimizer.

Im Detail: XLA

In TensorFlow-Programmen werden Berechnungsgraphen definiert. Die TPU führt keinen Python-Code direkt aus, sondern den Berechnungsgraphen, der von Ihrem TensorFlow-Programm definiert wird. Im Hintergrund wird ein Compiler namens XLA (Accelerated Linear Algebra Compiler) verwendet, um den TensorFlow-Graphen mit Berechnungs-Nodes in TPU-Maschinencode zu transformieren. Dieser Compiler führt auch viele erweiterte Optimierungen an Ihrem Code und Ihrem Speicherlayout durch. Die Kompilierung erfolgt automatisch, wenn Arbeit an die TPU gesendet wird. Sie müssen XLA nicht explizit in Ihre Build-Kette aufnehmen.

edce61112cd57972.png

Abbildung: Damit der von Ihrem TensorFlow-Programm definierte Berechnungsgraph auf einer TPU ausgeführt werden kann, wird er zuerst in eine XLA-Darstellung (Accelerated Linear Algebra Compiler) übersetzt und dann von XLA in TPU-Maschinencode kompiliert.

TPUs in Keras verwenden

TPUs werden ab Tensorflow 2.1 über die Keras API unterstützt. Die Keras-Unterstützung funktioniert auf TPUs und TPU-Pods. Hier ist ein Beispiel, das auf TPU, GPU(s) und CPU funktioniert:

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

In diesem Code-Snippet gilt:

  • Mit TPUClusterResolver().connect() wird die TPU im Netzwerk gefunden. Es funktioniert ohne Parameter auf den meisten Google Cloud-Systemen (AI Platform-Jobs, Colaboratory, Kubeflow, Deep Learning-VMs, die mit dem Tool „ctpu up“ erstellt wurden). Diese Systeme wissen, wo sich ihre TPU befindet, da sie eine Umgebungsvariable namens TPU_NAME haben. Wenn Sie eine TPU manuell erstellen, legen Sie entweder die Umgebungsvariable TPU_NAME auf der VM fest, von der aus Sie sie verwenden, oder rufen Sie TPUClusterResolver mit expliziten Parametern auf: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy ist der Teil, der die Verteilung und den „All-Reduce“-Algorithmus zur Gradientensynchronisierung implementiert.
  • Die Strategie wird über einen Bereich angewendet. Das Modell muss im Bereich der Strategie definiert sein.
  • Die Funktion tpu_model.fit erwartet für das TPU-Training ein tf.data.Dataset-Objekt als Eingabe.

Häufige Aufgaben beim Portieren auf TPUs

  • Es gibt viele Möglichkeiten, Daten in ein TensorFlow-Modell zu laden. Für TPUs ist jedoch die Verwendung der tf.data.Dataset API erforderlich.
  • TPUs sind sehr schnell und die Aufnahme von Daten wird oft zum Engpass, wenn sie verwendet werden. Im Leistungsleitfaden für Cloud TPU finden Sie Tools, mit denen Sie Datenengpässe erkennen können, sowie weitere Leistungstipps.
  • int8- oder int16-Zahlen werden als int32 behandelt. Die TPU hat keine Hardware für Ganzzahlen, die mit weniger als 32 Bit arbeitet.
  • Einige TensorFlow-Vorgänge werden nicht unterstützt. Die Liste finden Sie hier. Diese Einschränkung gilt nur für den Trainingscode, d.h. für den Vorwärts- und Rückwärtsdurchlauf durch Ihr Modell. Sie können weiterhin alle TensorFlow-Operationen in Ihrer Dateneingabe-Pipeline verwenden, da sie auf der CPU ausgeführt werden.
  • tf.py_func wird auf TPUs nicht unterstützt.

4. Daten werden geladen

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

Wir arbeiten mit einem Dataset von Blumenbildern. Das Ziel ist, sie in fünf Blumentypen zu kategorisieren. Das Laden von Daten erfolgt über die tf.data.Dataset API. Sehen wir uns zuerst die API an.

Praxisorientiert

Öffnen Sie das folgende Notebook, führen Sie die Zellen aus (Umschalt + EINGABETASTE) und folgen Sie der Anleitung, wenn Sie das Label „WORK REQUIRED“ sehen.

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

Weitere Informationen

Dataset „Blumen“

Der Datensatz ist in 5 Ordner unterteilt. Jeder Ordner enthält Blumen einer Art. Die Ordner heißen „sunflowers“, „daisy“, „dandelion“, „tulips“ und „roses“. Die Daten werden in einem öffentlichen Bucket in Google Cloud Storage gehostet. Auszug:

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

Warum tf.data.Dataset?

Keras und TensorFlow akzeptieren Datasets in allen ihren Trainings- und Bewertungsfunktionen. Sobald Sie Daten in ein Dataset geladen haben, bietet die API alle gängigen Funktionen, die für Trainingsdaten für neuronale Netzwerke nützlich sind:

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

In diesem Artikel finden Sie Tipps zur Leistung und Best Practices für Datasets. Die Referenzdokumentation finden Sie hier.

Grundlagen von tf.data.Dataset

Daten werden in der Regel in mehreren Dateien bereitgestellt, hier in Bildern. Sie können ein Dataset mit Dateinamen erstellen, indem Sie Folgendes aufrufen:

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

Anschließend „ordnen“ Sie jedem Dateinamen eine Funktion zu, mit der die Datei in der Regel geladen und in tatsächliche Daten im Arbeitsspeicher decodiert wird:

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)

So iterieren Sie ein Dataset:

for data in my_dataset:
  print(data)

Datasets mit Tupeln

Beim überwachten Lernen besteht ein Trainings-Dataset in der Regel aus Paaren von Trainingsdaten und richtigen Antworten. Dazu kann die Decodierungsfunktion Tupel zurückgeben. Sie haben dann ein Dataset mit Tupeln, die zurückgegeben werden, wenn Sie es durchlaufen. Die zurückgegebenen Werte sind TensorFlow-Tensoren, die von Ihrem Modell verwendet werden können. Sie können .numpy() für sie aufrufen, um Rohwerte zu sehen:

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())

Fazit:Das Laden von Bildern einzeln ist langsam.

Wenn Sie dieses Dataset durchlaufen, werden Sie feststellen, dass Sie etwa 1–2 Bilder pro Sekunde laden können. Das ist zu langsam. Die Hardwarebeschleuniger, die wir für das Training verwenden, können ein Vielfaches dieser Rate bewältigen. Im nächsten Abschnitt erfahren Sie, wie wir das erreichen.

Lösung

Hier finden Sie das Notebook mit der Lösung. Sie können es verwenden, wenn Sie nicht weiterkommen.

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

Behandelte Themen

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 Datasets mit Tupeln
  • 😀 Datasets durchlaufen

Bitte gehen Sie diese Checkliste kurz durch.

5. Daten schnell laden

Die Hardwarebeschleuniger vom Typ Tensor Processing Unit (TPU), die wir in diesem Lab verwenden, sind sehr schnell. Die Herausforderung besteht oft darin, sie schnell genug mit Daten zu versorgen, damit sie beschäftigt bleiben. Google Cloud Storage (GCS) kann einen sehr hohen Durchsatz aufrechterhalten. Wie bei allen Cloud-Speichersystemen kostet das Herstellen einer Verbindung jedoch etwas Netzwerkverkehr. Daher ist es nicht ideal, wenn unsere Daten als Tausende von einzelnen Dateien gespeichert werden. Wir fassen sie in einer kleineren Anzahl von Dateien zusammen und nutzen die Leistungsfähigkeit von tf.data.Dataset, um parallel aus mehreren Dateien zu lesen.

Durchlesen

Der Code, mit dem Bilddateien geladen, auf eine gemeinsame Größe angepasst und dann in 16 TFRecord-Dateien gespeichert werden, befindet sich im folgenden Notebook. Bitte lesen Sie sie kurz durch. Die Ausführung ist nicht erforderlich, da für den Rest des Codelabs korrekt im TFRecord-Format formatierte Daten bereitgestellt werden.

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

Ideales Datenlayout für optimalen GCS-Durchsatz

Das TFRecord-Dateiformat

Das bevorzugte Dateiformat von TensorFlow zum Speichern von Daten ist das protobuf-basierte TFRecord-Format. Andere Serialisierungsformate sind ebenfalls möglich, aber Sie können ein Dataset direkt aus TFRecord-Dateien laden, indem Sie Folgendes schreiben:

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

Für eine optimale Leistung wird empfohlen, den folgenden komplexeren Code zu verwenden, um gleichzeitig aus mehreren TFRecord-Dateien zu lesen. Mit diesem Code wird parallel aus N Dateien gelesen. Die Datenreihenfolge wird zugunsten der Lesegeschwindigkeit ignoriert.

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

TFRecord-Übersicht

In TFRecords können drei Arten von Daten gespeichert werden: Byte-Strings (Liste von Bytes), 64-Bit-Ganzzahlen und 32-Bit-Gleitkommazahlen. Sie werden immer als Listen gespeichert. Ein einzelnes Datenelement ist eine Liste mit der Größe 1. Mit den folgenden Hilfsfunktionen können Sie Daten in TFRecords speichern.

Bytestrings schreiben

# 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))

Ganzzahlen schreiben

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

Gleitkommazahlen schreiben

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

TFRecord schreiben – mit den oben genannten Hilfsfunktionen

# 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())

Wenn Sie Daten aus TFRecords lesen möchten, müssen Sie zuerst das Layout der gespeicherten Datensätze deklarieren. In der Deklaration können Sie auf jedes benannte Feld als Liste mit fester oder variabler Länge zugreifen:

Aus TFRecords lesen

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)

Nützliche Code-Snippets:

einzelne Datenelemente lesen

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

Lesen von Listen mit fester Größe

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

Lesen einer variablen Anzahl von Datenelementen

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

VarLenFeature gibt einen spärlichen Vektor zurück. Nach dem Decodieren des TFRecord ist ein zusätzlicher Schritt erforderlich:

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

Es ist auch möglich, optionale Felder in TFRecords zu haben. Wenn Sie beim Lesen eines Felds einen Standardwert angeben, wird dieser zurückgegeben, wenn das Feld fehlt, anstatt ein Fehler.

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

Behandelte Themen

  • 🤔 Daten in Dateien aufteilen, um schnell über GCS darauf zuzugreifen
  • 😓 TFRecords schreiben. Haben Sie die Syntax schon wieder vergessen? Kein Problem. Speichern Sie diese Seite als Lesezeichen, um sie als Spickzettel zu verwenden.
  • 🤔 Dataset aus TFRecords mit TFRecordDataset laden

Bitte gehen Sie diese Checkliste kurz durch.

6. Glückwunsch!

Sie können jetzt Daten in eine TPU einlesen. Fahren Sie mit dem nächsten Lab fort.

TPUs in der Praxis

TPUs und GPUs sind in Cloud AI Platform verfügbar:

Und schließlich freuen wir uns über Feedback. Bitte teilen Sie uns mit, wenn Sie in diesem Lab etwas Ungewöhnliches feststellen oder wenn Sie der Meinung sind, dass es verbessert werden sollte. Feedback kann über GitHub-Probleme [ Feedback-Link] gegeben werden.

HR.png

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