Pipelines de données à la vitesse des TPU: tf.data.Dataset et TFRecords

Pipelines de données à la vitesse des TPU:
tf.data.Dataset et TFRecords

À propos de cet atelier de programmation

subjectDernière mise à jour : oct. 4, 2021
account_circleRédigé par Martin Görner - @martin_gorner

1. Présentation

Les TPU sont très rapides. Le flux de données d'entraînement doit suivre le rythme de leur vitesse d'entraînement. Dans cet atelier, vous allez apprendre à charger des données à partir de GCS avec l'API tf.data.Dataset pour alimenter votre TPU.

Cet atelier est la première partie de la série "Keras sur TPU" de la série. Vous pouvez les effectuer dans l'ordre suivant ou indépendamment.

ca8cc21f6838eccc.png

Points abordés

  • Utiliser l'API tf.data.Dataset pour charger les données d'entraînement
  • Utiliser le format TFRecord pour charger efficacement les données d'entraînement à partir de GCS

Commentaires

Si vous constatez une anomalie dans cet atelier de programmation, veuillez nous en informer. Vous pouvez nous faire part de vos commentaires via GitHub [lien de commentaires].

2. Guide de démarrage rapide de Google Colaboratory

Cet atelier utilise Google Colaboratory et ne nécessite aucune configuration de votre part. Colaboratory est une plate-forme de notebooks en ligne destinée à l'enseignement. Il propose un entraînement sans frais sur les processeurs, les GPU et les TPU.

688858c21e3beff2.png

Vous pouvez ouvrir cet exemple de notebook et l'exécuter sur quelques cellules pour vous familiariser avec Colaboratory.

c3df49e90e5a654f.png Welcome to Colab.ipynb

Sélectionner un backend TPU

8832c6208c99687d.png

Dans le menu Colab, sélectionnez Environnement d'exécution > Modifiez le type d'environnement d'exécution, puis sélectionnez "TPU". Dans cet atelier de programmation, vous allez utiliser un TPU (Tensor Processing Unit) puissant sauvegardé pour l'entraînement avec accélération matérielle. La connexion à l'environnement d'exécution se fera automatiquement lors de la première exécution. Vous pouvez également utiliser la commande dans le coin supérieur droit.

Exécution du notebook

76d05caa8b4db6da.png

Pour exécuter les cellules une par une, cliquez dessus et utilisez Maj + ENTRÉE. Vous pouvez également exécuter l'intégralité du notebook avec Environnement d'exécution > Tout exécuter

Sommaire

429f106990037ec4.png

Tous les notebooks comportent une table des matières. Vous pouvez l'ouvrir à l'aide de la flèche noire située à gauche.

Cellules masquées

edc3dba45d26f12a.png

Certaines cellules n'affichent que leur titre. Cette fonctionnalité de notebook spécifique à Colab. Vous pouvez double-cliquer dessus pour voir le code à l'intérieur, mais ce n'est généralement pas très intéressant. Elles sont généralement compatibles avec les fonctions de visualisation ou de compatibilité. Vous devez quand même exécuter ces cellules pour que les fonctions qu'elles contiennent soient définies.

Authentification

cdd4b41413100543.png

Colab peut accéder à vos buckets Google Cloud Storage privés à condition que vous vous authentifiiez avec un compte autorisé. L'extrait de code ci-dessus déclenche un processus d'authentification.

3. [INFO] Que sont les Tensor Processing Units (TPU) ?

En résumé

f88cf6facfc70166.png

Le code pour entraîner un modèle sur TPU dans Keras (et utiliser le GPU ou le processeur si aucun TPU n'est disponible):

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

Aujourd'hui, nous allons utiliser des TPU pour créer et optimiser un classificateur de fleurs à des vitesses interactives (minutes par session d'entraînement).

688858c21e3beff2.png

Pourquoi les TPU ?

Les GPU modernes sont organisés autour de "cœurs" programmables, une architecture très flexible qui leur permet de gérer diverses tâches telles que le rendu 3D, le deep learning, les simulations physiques, etc. À l'inverse, les TPU associent un processeur vectoriel classique à une unité de multiplication matricielle dédiée et excellent dans toute tâche où de grandes multiplications de matrices dominent, comme les réseaux de neurones.

8eb3e718b8e2ed08.png

Illustration: couche de réseau de neurones dense sous forme de multiplication matricielle, avec un lot de huit images traitées simultanément via le réseau de neurones. Veuillez effectuer une multiplication sur une ligne par colonne pour vérifier qu'il s'agit bien d'une somme pondérée de toutes les valeurs en pixels d'une image. Les couches convolutives peuvent également être représentées par des multiplications matricielles, bien que cela soit un peu plus compliqué ( explication ici, section 1).

Matériel

MXU et VPU

Un cœur de TPU v2 est composé d'une unité matricielle (MXU, Matrix Multiply Unit) qui exécute les multiplications matricielles et d'une unité de traitement vectoriel (VPU) pour toutes les autres tâches telles que les activations, softmax, etc. Le VPU gère les calculs float32 et int32. Les unités matricielles, quant à elles, fonctionnent dans un format à virgule flottante 16-32 bits de précision mixte.

7d68944718f76b18.png

Valeurs à virgule flottante de précision mixte et bfloat16

L'unité matricielle calcule les multiplications matricielles à l'aide des entrées bfloat16 et des sorties float32. Les accumulations intermédiaires sont effectuées avec une précision de type float32.

19c5fc432840c714.png

L'entraînement des réseaux de neurones est généralement résistant au bruit introduit par une précision réduite à virgule flottante. Dans certains cas, le bruit contribue même à la convergence de l'optimiseur. La précision à virgule flottante 16 bits est traditionnellement utilisée pour accélérer les calculs, mais les formats float16 et float32 ont des plages très différentes. Réduire la précision de float32 à float16 entraîne généralement des sur-débits et des dépassements de capacité insuffisants. Des solutions existent, mais un travail supplémentaire est généralement nécessaire pour faire fonctionner float16.

C'est pourquoi Google a introduit le format bfloat16 dans les TPU. bfloat16 est une valeur float32 tronquée avec exactement les mêmes bits d'exposant et la même plage que float32. En plus du fait que les TPU calculent les multiplications matricielles en précision mixte avec des entrées bfloat16 mais en sorties float32, aucune modification du code n'est généralement nécessaire pour bénéficier des gains de performances liés à une précision réduite.

Tableau systolique

L'unité matricielle implémente les multiplications matricielles dans le matériel à l'aide d'un "tableau systolique" architecture dans laquelle les éléments de données circulent à travers un tableau d'unités de calcul matérielles. (En médecine, le terme « systolique » fait référence aux contractions cardiaques et au flux sanguin, ici au flux de données.)

L'élément de base d'une multiplication matricielle est un produit scalaire entre une ligne d'une matrice et une colonne de l'autre matrice (voir l'illustration en haut de cette section). Pour une multiplication matricielle Y=X*W, un élément du résultat serait:

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]

Sur un GPU, il est possible de programmer ce produit scalaire dans un "cœur" de GPU. et l'exécuter sur autant de "cœurs" disponibles en parallèle pour essayer de calculer chaque valeur de la matrice résultante en une seule fois. Si la matrice résultante fait 128 x 128, il faudrait 128 x 128=16 000 cœurs ce qui n'est généralement pas possible. Les GPU les plus volumineux possèdent environ 4 000 cœurs. En revanche, un TPU utilise le strict minimum de matériel pour les unités de calcul de l'unité matricielle: seulement bfloat16 x bfloat16 => float32 accumulateurs de multiplication, rien d'autre. Elles sont si petites qu'un TPU peut en implémenter 16K dans une unité matricielle de 128 x 128 et traiter cette multiplication matricielle en une seule fois.

f1b283fc45966717.gif

Illustration: tableau systolique MXU. Les éléments de calcul sont des accumulateurs. Les valeurs d'une matrice sont chargées dans le tableau (points rouges). Les valeurs de l'autre matrice circulent dans le tableau (points gris). Les lignes verticales propagent les valeurs vers le haut. Les lignes horizontales propagent des sommes partielles. Il revient à l'utilisateur de vérifier qu'au fur et à mesure que les données circulent dans le tableau, vous obtenez le résultat de la multiplication matricielle depuis le côté droit.

De plus, alors que les produits scalaires sont calculés dans une unité matricielle, les sommes intermédiaires sont simplement transférées entre des unités de calcul adjacentes. Ils n'ont pas besoin d'être stockés et récupérés vers/depuis la mémoire ou même un fichier d'enregistrement. Au final, l'architecture de tableau systolique des TPU présente un avantage significatif en termes de densité et de puissance, ainsi qu'un avantage non négligeable en termes de vitesse par rapport à un GPU lors du calcul des multiplications matricielles.

Cloud TPU

Lorsque vous demandez un " Cloud TPU v2" sur Google Cloud Platform, vous disposez d'une machine virtuelle (VM) dotée d'une carte TPU PCI. La carte TPU est équipée de quatre puces TPU double cœur. Chaque cœur de TPU comporte un VPU (Vector Processing Unit) et une unité de multiplication matriX de 128 x 128 MXU. Ce "Cloud TPU" est généralement connectée via le réseau à la VM à l'origine de la demande. La vue d'ensemble ressemble donc à ceci:

dfce5522ed644ece.png

Illustration: votre VM avec un Cloud TPU connecté au réseau accélérateur. "Cloud TPU" elle-même est composée d'une VM dotée d'une carte TPU PCI équipée de quatre puces TPU double cœur.

Pods TPU

Dans les centres de données de Google, les TPU sont connectés à une interconnexion de calcul hautes performances (HPC, High Performance Computing), qui peut les faire apparaître comme un accélérateur très important. Ils sont appelés "pods", et peuvent englober jusqu'à 512 cœurs de TPU v2 ou 2 048 cœurs de TPU v3.

2ec1e0d341e7fc34.jpeg

Illustration: un pod TPU v3. Racks et cartes TPU connectés via une interconnexion HPC.

Pendant l'entraînement, les gradients sont échangés entre les cœurs de TPU à l'aide de l'algorithme all-reduce ( une bonne explication ici). Le modèle en cours d'entraînement peut tirer parti du matériel en s'entraînant sur des lots de grande taille.

d97b9cc5d40fdb1d.gif

Illustration: synchronisation des gradients pendant l'entraînement à l'aide de l'algorithme all-reduce sur le réseau HPC de maillage toroïdal 2D de Google TPU.

Logiciel

Entraînement de lots de grande taille

La taille de lot idéale pour les TPU est de 128 éléments de données par cœur de TPU, mais le matériel peut déjà présenter une bonne utilisation à partir de 8 éléments de données par cœur de TPU. Rappelez-vous qu'un Cloud TPU possède huit cœurs.

Dans cet atelier de programmation, nous allons utiliser l'API Keras. Dans Keras, le lot que vous spécifiez correspond à la taille de lot globale pour l'ensemble du TPU. Vos lots seront automatiquement divisés en huit et exécutés sur les huit cœurs du TPU.

da534407825f01e3.png

Pour obtenir d'autres conseils sur les performances, consultez le Guide sur les performances des TPU. Pour les très grandes tailles de lot, une attention particulière peut être nécessaire dans certains modèles. Pour en savoir plus, consultez la section LARSOptimizer.

Sous le capot: XLA

Les programmes TensorFlow définissent des graphiques de calcul. Le TPU n'exécute pas directement du code Python, mais le graphe de calcul défini par votre programme TensorFlow. En arrière-plan, un compilateur appelé XLA (Acceleated Linear Algebra compiler) transforme le graphe TensorFlow des nœuds de calcul en code de machine TPU. Ce compilateur effectue également de nombreuses optimisations avancées sur votre code et la disposition de votre mémoire. La compilation s'effectue automatiquement à mesure que le travail est envoyé au TPU. Vous n'avez pas besoin d'inclure explicitement XLA dans votre chaîne de compilation.

edce61112cd57972.png

Illustration: Pour s'exécuter sur TPU, le graphe de calcul défini par votre programme TensorFlow est d'abord traduit en représentation XLA (acceleated Linear Algebra compiler), puis compilé par XLA en code machine TPU.

Utiliser des TPU dans Keras

Les TPU sont compatibles avec l'API Keras depuis TensorFlow 2.1. La compatibilité avec Keras fonctionne sur les TPU et les pods TPU. Voici un exemple qui fonctionne sur les TPU, les GPU et les processeurs:

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

Dans cet extrait de code:

  • TPUClusterResolver().connect() trouve le TPU sur le réseau. Elle fonctionne sans paramètres sur la plupart des systèmes Google Cloud (tâches AI Platform, Colaboratory, Kubeflow, VM de deep learning créées via l'utilitaire "gsutil up"). Ces systèmes savent où se trouve leur TPU grâce à une variable d'environnement TPU_NAME. Si vous créez un TPU manuellement, définissez l'environnement TPU_NAME. "var." sur la VM à partir de laquelle vous l'utilisez, ou appelez TPUClusterResolver avec des paramètres explicites: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy est la partie qui implémente la distribution et la fonction "all-reduce" ; l'algorithme de synchronisation de gradient.
  • La stratégie est appliquée via un champ d'application. Le modèle doit être défini dans le champ d'application de la stratégie.
  • La fonction tpu_model.fit attend un objet tf.data.Dataset en entrée pour l'entraînement TPU.

Tâches de portage TPU courantes

  • Bien qu'il existe de nombreuses façons de charger des données dans un modèle TensorFlow, pour les TPU, l'utilisation de l'API tf.data.Dataset est requise.
  • Les TPU sont très rapides et l'ingestion de données devient souvent le goulot d'étranglement lors de leur exécution. Le guide sur les performances TPU propose des outils permettant de détecter les goulots d'étranglement des données, ainsi que d'autres conseils de performances.
  • Les nombres int8 ou int16 sont traités comme des int32. Le TPU n'a pas de matériel entier fonctionnant sur moins de 32 bits.
  • Certaines opérations TensorFlow ne sont pas compatibles. Cliquez ici pour consulter la liste. Heureusement, cette limitation ne s'applique qu'au code d'entraînement, c'est-à-dire aux propagations avant et arrière dans votre modèle. Vous pouvez toujours utiliser toutes les opérations Tensorflow dans votre pipeline d'entrée de données, car elles seront exécutées sur le processeur.
  • tf.py_func n'est pas compatible avec TPU.

4. Chargement des données…

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

Nous allons travailler avec un ensemble de données de photos de fleurs. L'objectif est d'apprendre à les classer en cinq types de fleurs. Le chargement des données est effectué à l'aide de l'API tf.data.Dataset. Tout d'abord, familiarisez-vous avec l'API.

Activités pratiques

Veuillez ouvrir le notebook suivant, exécuter les cellules (Maj-ENTRÉE) et suivre les instructions dès que le message "TÂCHE REQUISE" s'affiche libellé.

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

Informations supplémentaires

À propos des "fleurs" ensemble de données

Le jeu de données est organisé en cinq dossiers. Chaque dossier contient une sorte de fleurs. Les dossiers sont appelés "tournesols", "marguerite", "pissenlit", "tulipes" et "roses". Les données sont hébergées dans un bucket public sur Google Cloud Storage. Extrait:

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

Pourquoi utiliser tf.data.Dataset ?

Keras et Tensorflow acceptent les ensembles de données dans toutes leurs fonctions d'entraînement et d'évaluation. Une fois que vous avez chargé des données dans un ensemble de données, l'API offre toutes les fonctionnalités courantes utiles pour les données d'entraînement des réseaux de neurones:

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

Vous trouverez des conseils sur les performances et les bonnes pratiques concernant les ensembles de données dans cet article. Pour accéder à la documentation de référence, cliquez ici.

Principes de base de tf.data.Dataset

Les données sont généralement incluses dans plusieurs fichiers, ici des images. Vous pouvez créer un ensemble de données de noms de fichiers en appelant la méthode suivante:

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

Vous « mapper » ensuite une fonction à chaque nom de fichier, qui charge généralement et décode le fichier en données réelles en mémoire:

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)

Pour itérer sur un ensemble de données:

for data in my_dataset:
 
print(data)

Ensembles de données de tuples

Dans l'apprentissage supervisé, un ensemble de données d'entraînement est généralement constitué de paires de données d'entraînement et de bonnes réponses. Pour ce faire, la fonction de décodage peut renvoyer des tuples. Vous disposerez alors d'un ensemble de données composé de tuples. Les tuples seront renvoyés lorsque vous effectuerez une itération sur celui-ci. Les valeurs renvoyées sont des Tensors TensorFlow prêts à être utilisés par votre modèle. Vous pouvez appeler .numpy() sur ces nœuds pour afficher les valeurs brutes:

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

Conclusion:charger les images une par une est lent !

À mesure que vous itérez sur cet ensemble de données, vous verrez que vous pouvez charger quelque chose comme 1 à 2 images par seconde. Trop lent ! Les accélérateurs matériels que nous utiliserons pour l'entraînement peuvent supporter bien ce débit. Passez à la section suivante pour voir comment nous y parviendrons.

Solution

Voici le notebook de la solution. Vous pouvez l'utiliser si vous êtes bloqué.

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

Points abordés

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 Ensembles de données de tuples
  • 😀 l'itération sur les ensembles de données

Veuillez prendre un moment pour passer en revue cette liste de contrôle.

5. Chargement rapide des données

Les accélérateurs matériels TPU (Tensor Processing Unit) que nous allons utiliser dans cet atelier sont très rapides. Le défi consiste souvent à leur fournir des données suffisamment rapidement pour qu'ils occupent une place importante. Google Cloud Storage (GCS) est capable de supporter un débit très élevé, mais comme avec tous les systèmes de stockage cloud, l'initiation d'une connexion coûte une partie du réseau. Par conséquent, stocker nos données sous la forme de milliers de fichiers individuels n'est pas idéal. Nous allons les regrouper dans un plus petit nombre de fichiers et utiliser la puissance de tf.data.Dataset pour lire plusieurs fichiers en parallèle.

Lecture

Le code qui charge les fichiers image, les redimensionne à une taille commune, puis les stocke dans 16 fichiers TFRecord se trouve dans le notebook suivant. Veuillez le lire rapidement. Cette opération n'est pas nécessaire, car des données correctement formatées TFRecord seront fournies pour le reste de l'atelier de programmation.

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

Disposition de données idéale pour un débit GCS optimal

Format de fichier TFRecord

Le format de fichier privilégié de TensorFlow pour stocker des données est le format TFRecord basé sur protobuf. D'autres formats de sérialisation fonctionneraient également, mais vous pouvez charger un jeu de données directement à partir de fichiers TFRecord en écrivant:

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

Pour des performances optimales, nous vous recommandons d'utiliser le code plus complexe suivant pour lire plusieurs fichiers TFRecord à la fois. Ce code lit les N fichiers en parallèle et ignore l'ordre des données afin d'améliorer la vitesse de lecture.

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

Aide-mémoire TFRecord

Trois types de données peuvent être stockés dans des fichiers TFRecord: les chaînes d'octets (liste d'octets), les entiers 64 bits et les floats 32 bits. Elles sont toujours stockées sous forme de listes. Un seul élément de données correspond à une liste de taille 1. Vous pouvez utiliser les fonctions d'assistance suivantes pour stocker des données dans des TFRecords.

Écrire des chaînes d'octets

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

Écrire des entiers

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

écrire des floats

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

Écrire un fichier TFRecord à l'aide des outils d'aide ci-dessus

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

Pour lire des données à partir de TFRecords, vous devez d'abord déclarer la mise en page des enregistrements que vous avez stockés. Dans la déclaration, vous pouvez accéder à n'importe quel champ nommé en tant que liste de longueur fixe ou liste de longueur variable:

Lecture à partir de TFRecords

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)

Extraits de code utiles:

lire des éléments de données uniques

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

lire des listes d'éléments de taille fixe

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

Lire un nombre variable d'éléments de données

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

Un VarLenFeature renvoie un vecteur creux et une étape supplémentaire est requise après le décodage du TFRecord:

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

Il est également possible d'avoir des champs facultatifs dans les fichiers TFRecord. Si vous spécifiez une valeur par défaut lors de la lecture d'un champ, cette valeur est renvoyée à la place d'une erreur si le champ est manquant.

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

Points abordés

  • 🤔 Segmenter des fichiers de données pour un accès rapide depuis GCS
  • 👀 comment écrire des TFRecords. (Vous avez déjà oublié la syntaxe ? Pas de problème, ajoutez cette page à vos favoris en tant qu'aide-mémoire.)
  • 🤔 Charger un ensemble de données à partir de TFRecords avec TFRecordDataset

Veuillez prendre un moment pour passer en revue cette liste de contrôle.

6. Félicitations !

Vous pouvez désormais alimenter un TPU en données. Veuillez passer à l'atelier suivant

Les TPU en pratique

Les TPU et les GPU sont disponibles sur Cloud AI Platform:

Enfin, les commentaires sont les bienvenus. Veuillez nous indiquer si vous constatez une anomalie dans cet atelier ou si vous pensez qu'elle doit être améliorée. Vous pouvez nous faire part de vos commentaires via GitHub [lien de commentaires].

HR.png

Identifiant Martin Görner small.jpg
Auteur: Martin Görner
Twitter: @martin_gorner