Migrer de l'API Estimator vers l'API TPUEstimator
Ce tutoriel explique comment convertir un programme de modèle utilisant l'API Estimator en un programme utilisant l'API TPUEstimator.
Avertissement : L'API TPUEstimator n'est compatible qu'avec TensorFlow 1.x. Si vous écrivez un modèle avec TensorFlow 2.x, utilisez plutôt Keras.
Présentation
Les programmes de modèle utilisant l'API TPUEstimator peuvent bénéficier pleinement des Tensor Processing Units (TPU), tout en restant compatibles avec les processeurs et les GPU.
Après avoir terminé ce tutoriel, vous saurez :
- convertir votre code utilisant l'API Estimator en un code utilisant l'API TPUEstimator ;
- exécuter des prédictions sur Cloud TPU.
Avant de commencer
Avant de commencer ce tutoriel, vérifiez que votre projet Google Cloud est correctement configuré.
Ce tutoriel utilise des composants facturables de Google Cloud. Consultez la grille tarifaire de Cloud TPU pour estimer vos coûts. Veillez à nettoyer les ressources que vous avez créées lorsque vous avez terminé, afin d'éviter des frais inutiles.
Configurer vos ressources
Cette section fournit des informations sur la configuration des ressources de stockage Cloud Storage, des ressources de VM et des ressources Cloud TPU pour les tutoriels.
Créer un bucket Cloud Storage
Vous avez besoin d'un bucket Cloud Storage pour stocker les données permettant d'entraîner votre modèle et les résultats de l'entraînement. La commande gcloud
utilisée dans ce tutoriel configure les autorisations par défaut du compte de service Cloud TPU. Si vous souhaitez utiliser des autorisations plus précises, vérifiez les autorisations de niveau d'accès.
L'emplacement du bucket doit se trouver dans la même région que votre machine virtuelle (VM) et votre nœud TPU. Les VM et les nœuds TPU sont situés dans des zones spécifiques, qui sont des subdivisions au sein d'une région.
Accédez à la page Cloud Storage de Google Cloud Console.
Créez un bucket en spécifiant les options suivantes :
- Un nom unique de votre choix
- Sélectionnez
Region
pour le type d'emplacement etus-central1
pour l'emplacement (zone). - Classe de stockage par défaut :
Standard
- Emplacement : spécifiez un emplacement de bucket dans la région où vous prévoyez de créer votre nœud TPU. Consultez la section Types et zones des TPU pour savoir où les différents types de TPU sont disponibles.
Créer un TPU et une VM
Les ressources TPU sont composées d'une machine virtuelle (VM) et d'un Cloud TPU portant le même nom. Ces ressources doivent se trouver dans la même région/zone que le bucket que vous venez de créer.
Vous pouvez configurer la VM et vos ressources TPU à l'aide des commandes gcloud
ou de la console Cloud. Pour en savoir plus sur la gestion des ressources TPU, consultez la page Créer et supprimer des TPU.
Ouvrez une fenêtre Cloud Shell.
Configurez
gcloud
pour utiliser votre projet. projet dans lequel vous souhaitez créer Cloud TPU.$ gcloud config set project your-project
Lancez une VM Compute Engine et Cloud TPU à l'aide de la commande
gcloud
.$ gcloud compute tpus execution-groups create \ --name=tpu-name \ --zone=europe-west4-a \ --tf-version=2.12.0 \ --machine-type=n1-standard-1 \ --accelerator-type=v3-8
Description des options de commande
name
- Nom du Cloud TPU à créer.
zone
- Zone dans laquelle vous prévoyez de créer votre Cloud TPU.
tf-version
- Version de Tensorflow que la commande
gcloud
installe sur votre VM. machine-type
- Type de machine de la VM Compute Engine à créer.
accelerator-type
- Le type de Cloud TPU à créer.
Pour en savoir plus sur la commande
gcloud
, consultez la documentation de référence de gcloud.Une fois l'exécution de la commande
gcloud compute tpus execution-groups
terminée, vérifiez que l'invite de l'interface système est passée deusername@projectname
àusername@vm-name
. Cette modification indique que vous êtes maintenant connecté à votre VM Compute Engine.gcloud compute ssh tpu-name --zone=europe-west4-a
À mesure que vous appliquez ces instructions, exécutez chaque commande commençant par (vm)$
dans la fenêtre de session de la VM.
Installer des pandas
Installez ou mettez à niveau des pandas à l'aide de la commande suivante :
pip install pandas
Définir les hyperparamètres
Dans cette section de code, vous ajoutez plusieurs hyperparamètres requis par les TPU. Vous ajoutez ces hyperparamètres en tant qu'options à votre script d'entraînement, ce qui vous permet de les modifier au moment de l'exécution.
Les paramètres que vous ajoutez sont les suivants :
- tpu. Ce paramètre identifie le nom ou l'adresse IP du nœud TPU sur lequel le modèle doit être exécuté.
- model_dir. Il s'agit du chemin d'accès pour sauvegarder les points de contrôle du modèle. Ce chemin d'accès doit être un bucket Cloud Storage.
- iterations. Il s'agit du nombre d'itérations par boucle d'entraînement.
- use_tpu. Indique si vous souhaitez exécuter le modèle sur des TPU ou des GPU/CPU en fonction de la disponibilité.
API Estimator
# Model specific parameters
tf.flags.DEFINE_integer("batch_size",
default=50,
help="Batch size.")
tf.flags.DEFINE_integer("train_steps",
default=1000,
help="Total number of training steps.")
FLAGS = tf.flags.FLAGS
API TPUEstimator
# Cloud TPU Cluster Resolver flags
tf.flags.DEFINE_string(
"tpu", default=None,
help="The Cloud TPU to use for training. This should be the name used when "
"creating the Cloud TPU. To find out the name of TPU, either use command "
"'gcloud compute tpus list --zone=<zone-name>', or use "
"'ctpu status --details' if you have created your Cloud TPU using 'ctpu up'.")
# Model specific parameters
tf.flags.DEFINE_string(
"model_dir", default="",
help="This should be the path of storage bucket which will be used as "
"model_directory to export the checkpoints during training.")
tf.flags.DEFINE_integer(
"batch_size", default=128,
help="This is the global batch size and not the per-shard batch.")
tf.flags.DEFINE_integer(
"train_steps", default=1000,
help="Total number of training steps.")
tf.flags.DEFINE_integer(
"eval_steps", default=4,
help="Total number of evaluation steps. If `0`, evaluation "
"after training is skipped.")
# TPU specific parameters.
tf.flags.DEFINE_bool(
"use_tpu", default=True,
help="True, if want to run the model on TPU. False, otherwise.")
tf.flags.DEFINE_integer(
"iterations", default=500,
help="Number of iterations per TPU training loop.")
Charger les données
Cette section de code spécifie comment lire et charger les données.
Les TPU prennent en charge les types de données suivants :
tf.float32
tf.complex64
tf.int64
tf.bool
tf.bfloat64
API Estimator
def load_data(y_name='Species'):
"""Returns the iris dataset as (train_x, train_y), (test_x, test_y)."""
train_path, test_path = maybe_download()
train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
train_x, train_y = train, train.pop(y_name)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)
test_x, test_y = test, test.pop(y_name)
return (train_x, train_y), (test_x, test_y)
API TPUEstimator
def load_data(y_name='Species'):
"""Returns the iris dataset as (train_x, train_y), (test_x, test_y)."""
train_path, test_path = maybe_download()
train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0,
dtype={'SepalLength': pd.np.float32,
'SepalWidth': pd.np.float32,
'PetalLength': pd.np.float32,
'PetalWidth': pd.np.float32,
'Species': pd.np.int32})
train_x, train_y = train, train.pop(y_name)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0,
dtype={'SepalLength': pd.np.float32,
'SepalWidth': pd.np.float32,
'PetalLength': pd.np.float32,
'PetalWidth': pd.np.float32,
'Species': pd.np.int32})
test_x, test_y = test, test.pop(y_name)
return (train_x, train_y), (test_x, test_y)
Définir les fonctions d'entrée
Une différence essentielle entre l'API Estimator et l'API TPUEstimator réside dans la signature de la fonction des fonctions d'entrée. Avec l'API Estimator, vous pouvez écrire des fonctions d'entrée avec un nombre quelconque de paramètres. Avec l'API TPUEstimator, les fonctions d'entrée ne peuvent intégrer qu'un seul paramètre, params
. Cette valeur params
contient toutes les paires clé/valeur de l'objet TPUEstimator, ainsi que des clés supplémentaires telles que batch_size
.
Une façon de remédier à cette différence consiste à utiliser les fonctions lambda lors de l'appel des fonctions d'entrée. Avec les fonctions lambda, vous ne devez apporter que des modifications mineures aux fonctions d'entrée existantes.
Les sections suivantes montrent comment mettre à jour vos fonctions d'entrée. Vous verrez plus tard comment utiliser les fonctions lambda pour convertir ces fonctions d'entrée afin qu'elles fonctionnent avec l'API TPUEstimator.
Fonction d'entrée d'entraînement
Avec l'API TPUEstimator, votre fonction d'entrée pour l'entraînement, train_input_fn
, doit renvoyer un nombre d'échantillons d'entrées pouvant être divisé par le nombre de cœurs Cloud TPU. Par exemple, si vous utilisez 8 cœurs, chaque taille de lot doit être divisible par 8.
Pour ce faire, le code précédent utilise la fonction dataset.batch(batch_size, drop_remainder=True)
. Cette fonction effectue des traitements par lot à l'aide du paramètre batch_size
et élimine le reste.
API Estimator
def train_input_fn(features, labels, batch_size):
"""An input function for training"""
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)
# Return the dataset.
return dataset
API TPUEstimator
def train_input_fn(features, labels, batch_size):
"""An input function for training."""
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat()
dataset = dataset.batch(batch_size, drop_remainder=True)
# Return the dataset.
return dataset
Fonction d'entrée d'évaluation
Au cours de cette étape, vous mettez à jour la fonction d'entrée d'évaluation eval_input_fn
pour vous assurer que le nombre d'échantillons d'entrée peut être divisé par le nombre de cœurs TPU. Pour ce faire, utilisez la fonction dataset.batch(batch_size, drop_remainder=True)
.
API Estimator
def eval_input_fn(features, labels, batch_size):
"""An input function for evaluation or prediction"""
features=dict(features)
if labels is None:
# No labels, use only features.
inputs = features
else:
inputs = (features, labels)
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices(inputs)
# Batch the examples
assert batch_size is not None, "batch_size must not be None"
dataset = dataset.batch(batch_size)
# Return the dataset.
return dataset
API TPUEstimator
def eval_input_fn(features, labels, batch_size):
"""An input function for evaluation."""
features = dict(features)
inputs = (features, labels)
# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices(inputs)
dataset = dataset.shuffle(1000).repeat()
dataset = dataset.batch(batch_size, drop_remainder=True)
# Return the dataset.
return dataset
Fonction d'entrée de prédiction
Pour les prédictions dans TPUEstimators, l'ensemble de données d'entrée doit comporter des tenseurs ayant pour dimension externe supplémentaire le paramètre batch_size
. Par conséquent, vous devez ajouter une fonction d'entrée de prédiction, qui utilise les paramètres features
et batch_size
. Cette fonction vous permet d'avoir moins d'échantillons d'entrée qu'avec le paramètre batch_size
.
Si vous utilisez l'API Estimator, la fonction d'entrée de prédiction est facultative.
API Estimator
Une fonction d'entrée de prédiction est facultative pour l'API Estimator, car la fonction d'évaluation eval_input_fn effectue cette tâche.
API TPUEstimator
def predict_input_fn(features, batch_size):
"""An input function for prediction."""
dataset = tf.data.Dataset.from_tensor_slices(features)
dataset = dataset.batch(batch_size)
return dataset
Mettre à jour la fonction de modèle personnalisé
Votre prochaine tâche consiste à mettre à jour la fonction de modèle personnalisé :
- Remplacez les instances de
tf.estimator.EstimatorSpec
pour utilisertf.contrib.tpu.TPUEstimatorSpec
. - Supprimez toutes les instances de
tf.summary
. L'API TPUEstimator ne prend pas en charge les résumés personnalisés pour Tensorboard. Cependant, les résumés de base sont automatiquement enregistrés dans les fichiers d'événements du répertoire du modèle. - Encapsulez l'optimiseur à l'aide de
tf.contrib.tpu.CrossShardOptimizer
.CrossShardOptimizer
comporte un paramètreallreduce
permettant d'agréger les gradients et de diffuser le résultat à chaque segment.CrossShardOptimizer
n'étant pas compatible avec l'entraînement local, vous devez également vérifier l'indicateuruse_tpu
.
API Estimator
def my_model(features, labels, mode, params):
"""DNN with three hidden layers, and dropout of 0.1 probability."""
# Create three fully connected layers each layer having a dropout
# probability of 0.1.
net = tf.feature_column.input_layer(features, params['feature_columns'])
for units in params['hidden_units']:
net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
# Compute logits (1 per class).
logits = tf.layers.dense(net, params['n_classes'], activation=None)
# Compute predictions.
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
# Compute loss.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels,
logits=logits)
# Compute evaluation metrics.
accuracy = tf.metrics.accuracy(labels=labels,
predictions=predicted_classes,
name='acc_op')
metrics = {'accuracy': accuracy}
tf.summary.scalar('accuracy', accuracy[1])
if mode == tf.estimator.ModeKeys.EVAL:
return tf.estimator.EstimatorSpec(
mode, loss=loss, eval_metric_ops=metrics)
# Create training op.
if mode == tf.estimator.ModeKeys.TRAIN
optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
API TPUEstimator
def my_model(features, labels, mode, params):
"""Deep Neural Network(DNN) model.
This is a DNN Model with 3 hidden layers. First 2 hidden layers are having
10 neurons in each. And number of neurons in the last layer is equal to the
number of output classes. This is a densely connected network where each
neuron of previous layer is connected to each neuron of next layer.
Args:
features: Feature values for input samples.
labels: label/class assigned to the corresponding input sample.
mode: "TRAIN"/"EVAL"/"PREDICT"
params: Dictionary used to pass extra parameters to model function from
the main function.
Returns:
TPUEstimatorSpec object.
"""
# Create three fully connected layers.
net = tf.feature_column.input_layer(features, params["feature_columns"])
for units in params["hidden_units"]:
net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
# Compute logits (1 per class).
logits = tf.layers.dense(net, params["n_classes"], activation=None)
# Compute predictions.
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
"class_ids": predicted_classes[:, tf.newaxis],
"probabilities": tf.nn.softmax(logits),
"logits": logits,
}
return tf.contrib.tpu.TPUEstimatorSpec(mode, predictions=predictions)
# Compute loss.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels,
logits=logits)
if mode == tf.estimator.ModeKeys.EVAL:
return tf.contrib.tpu.TPUEstimatorSpec(
mode=mode, loss=loss, eval_metrics=(metric_fn, [labels, logits]))
# Create training op.
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
if FLAGS.use_tpu:
optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer)
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.contrib.tpu.TPUEstimatorSpec(mode, loss=loss, train_op=train_op)
Ajouter une fonction de métrique d'évaluation
L'API Estimator et l'API TPUEstimator se différencient également dans leur mode de gestion des métriques. L'API Estimator permet de transmettre des métriques comme un dictionnaire normal, alors que l'API TPUEstimator requiert l'utilisation d'une fonction.
API Estimator
Facultatif. La fonction my_model
génère les statistiques.
API TPUEstimator
def metric_fn(labels, logits):
"""Function to return metrics for evaluation."""
predicted_classes = tf.argmax(logits, 1)
accuracy = tf.metrics.accuracy(labels=labels,
predictions=predicted_classes,
name="acc_op")
return {"accuracy": accuracy}
Mettre à jour la fonction principale
Configurer les TPU
Au cours de cette étape, vous allez configurer le cluster TPU.
Pour configurer le cluster, vous pouvez utiliser les valeurs attribuées aux hyperparamètres. Consultez la section Définir des hyperparamètres pour obtenir plus d'informations. De plus, vous devez définir les valeurs suivantes :
allow_soft_placement
. Lorsqu'il est défini sur "true", ce paramètre permet à TensorFlow d'utiliser un appareil GPU si un TPU n'est pas disponible. Si un appareil GPU est également indisponible, un appareil CPU est utilisé.log_device_placement
. Indique que TensorFlow doit consigner les emplacements des appareils.
API Estimator
Non requis, car cette section de code concerne uniquement les TPU.
API TPUEstimator
# Resolve TPU cluster and runconfig for this.
tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(
FLAGS.tpu)
run_config = tf.contrib.tpu.RunConfig(
model_dir=FLAGS.model_dir,
cluster=tpu_cluster_resolver,
session_config=tf.ConfigProto(
allow_soft_placement=True, log_device_placement=True),
tpu_config=tf.contrib.tpu.TPUConfig(FLAGS.iterations),
)
Ajouter des paramètres spécifiques de TPU au classificateur
Dans cette section du code, vous mettez à jour la variable de classificateur pour utiliser la classe TPUEstimator. Cette modification nécessite que vous ajoutiez les paramètres suivants :
use_tpu
train_batch_size
eval_batch_size
predict_batch_size
config
API Estimator
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.estimator.Estimator(
model_fn=my_model,
params={
'feature_columns': my_feature_columns,
# Two hidden layers of 10 nodes each.
'hidden_units': [10, 10],
# The model must choose between 3 classes.
'n_classes': 3,
})
API TPUEstimator
# Build 2 hidden layer DNN with 10, 10 units respectively.
classifier = tf.contrib.tpu.TPUEstimator(
model_fn=my_model,
use_tpu=FLAGS.use_tpu,
train_batch_size=FLAGS.batch_size,
eval_batch_size=FLAGS.batch_size,
predict_batch_size=FLAGS.batch_size,
config=run_config,
params={
# Name of the feature columns in the input data.
"feature_columns": my_feature_columns,
# Two hidden layers of 10 nodes each.
"hidden_units": [10, 10],
# The model must choose between 3 classes.
"n_classes": 3,
"use_tpu": FLAGS.use_tpu,
})
Appeler la méthode d'entraînement
Le prochain changement consiste à mettre à jour la méthode d'entraînement. Notez l'utilisation d'une fonction lambda pour appeler la fonction train_input_fn
. Cette méthodologie facilite l'utilisation de vos fonctions existantes avec l'API TPUEstimator.
En outre, vous devez modifier le paramètre steps (pas) par max_steps
. Dans la section suivante, réaffectez le paramètre Steps pour spécifier le nombre d'étapes d'évaluation.
API Estimator
# Train the Model.
classifier.train(
input_fn=lambda:iris_data.train_input_fn(
train_x, train_y, FLAGS.batch_size),
steps=FLAGS.train_steps)
API TPUEstimator
# Train the Model.
classifier.train(
input_fn=lambda params: iris_data.train_input_fn(
train_x, train_y, params["batch_size"]),
max_steps=FLAGS.train_steps)
Appeler la méthode d'évaluation
Ce changement est identique à celui que vous avez apporté à la méthode d'entraînement. Là encore, l'utilisation d'une fonction lambda facilite l'utilisation d'une fonction d'entrée d'évaluation existante.
En outre, vous devez remplacer le paramètre steps
par la valeur définie à partir de l'indicateur de ligne de commande eval_steps
.
API Estimator
# Evaluate the model.
eval_result = classifier.evaluate(
input_fn=lambda:iris_data.eval_input_fn(
test_x, test_y, FLAGS.batch_size))
print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))
API TPUEstimator
# Evaluate the model.
eval_result = classifier.evaluate(
input_fn=lambda params: iris_data.eval_input_fn(
test_x, test_y, params["batch_size"]),
steps=FLAGS.eval_steps)
Appeler la méthode de prédiction
Comme pour les méthodes d'entraînement et d'évaluation, vous devez mettre à jour la méthode de prédiction. Là encore, l'utilisation d'une fonction lambda facilite l'utilisation d'une fonction d'entrée d'évaluation existante.
API Estimator
# Generate predictions from the model
predictions = classifier.predict(
input_fn=lambda: iris_data.eval_input_fn(
iris_data.PREDICTION_INPUT_DATA,
labels=None,
batch_size=FLAGS.batch_size))
for pred_dict, expec in zip(predictions, iris_data.PREDICTION_OUTPUT_DATA):
template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')
class_id = pred_dict['class_ids'][0]
probability = pred_dict['probabilities'][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
API TPUEstimator
# Generate predictions from the model
predictions = classifier.predict(
input_fn=lambda params: iris_data.predict_input_fn(
iris_data.PREDICTION_INPUT_DATA, params["batch_size"]))
for pred_dict, expec in zip(predictions, iris_data.PREDICTION_OUTPUT_DATA):
template = ("\nPrediction is \"{}\" ({:.1f}%), expected \"{}\"")
class_id = pred_dict["class_ids"][0]
probability = pred_dict["probabilities"][class_id]
print(template.format(iris_data.SPECIES[class_id],
100 * probability, expec))
Effectuer un nettoyage
Pour éviter que les ressources utilisées dans cet article soient facturées sur votre compte GCP, suivez les étapes ci-dessous :
Déconnectez-vous de la VM Compute Engine :
(vm)$ exit
Votre invite devrait maintenant être
username@projectname
, indiquant que vous êtes dans Cloud Shell.Dans Cloud Shell, exécutez la commande
ctpu delete
avec l'option --zone utilisée lors de votre configuration Cloud TPU pour supprimer votre VM Compute Engine et votre Cloud TPU :$ ctpu delete [optional: --zone]
Exécutez
ctpu status
pour vérifier qu'il ne reste aucune instance allouée afin d'éviter des frais inutiles liés à l'utilisation des ressources TPU. La suppression peut prendre plusieurs minutes. Si vous n'avez plus d'instances allouées, une réponse de ce type s'affiche :$ ctpu status --zone=europe-west4-a
2018/04/28 16:16:23 WARNING: Setting zone to "--zone=europe-west4-a" No instances currently exist. Compute Engine VM: -- Cloud TPU: --
Exécutez
gsutil
comme indiqué, en remplaçant bucket-name par le nom du bucket Cloud Storage que vous avez créé pour ce tutoriel :$ gsutil rm -r gs://bucket-name
Étapes suivantes
Pour en savoir plus sur l'API Estimator et l'API TPUEstimator, consultez les rubriques suivantes :
- API Estimator
- API TPUEstimator