Migrazione dall'API stime all'API TPUEstimator

Questo tutorial descrive come convertire un programma modello utilizzando l'API Validator in uno utilizzando l'API TPUEstimator.

Avviso L'API TPUEstimator è supportata solo con Tensorflow 1.x. Se scrivi il tuo modello con Tensorflow 2.x, utilizza invece Keras.

Panoramica

I programmi di modello che utilizzano l'API TPUEstimator possono sfruttare al massimo le unità di elaborazione (TPU) Tensor, pur rimanendo compatibili con CPU e GPU.

Al termine di questo tutorial saprai:

  • Come convertire il codice, dall'utilizzo dell'API LTI all'API TPUEstimator
  • Come eseguire previsioni su Cloud TPU

Prima di iniziare

Prima di iniziare questo tutorial, verifica che il tuo progetto Google Cloud sia configurato correttamente.

Questa procedura dettagliata utilizza i componenti fatturabili di Google Cloud. Controlla la pagina dei prezzi di Cloud TPU per una stima dei costi. Al termine, pulisci le risorse che crei per evitare addebiti inutili.

Configurare le risorse

Questa sezione fornisce informazioni sulla configurazione delle risorse di archiviazione, VM e Cloud TPU di Cloud Storage per i tutorial.

Crea un bucket Cloud Storage

Ti serve un bucket Cloud Storage per archiviare i dati che utilizzi per addestrare il modello e i risultati dell'addestramento. Il comando gcloud utilizzato in questo tutorial configura le autorizzazioni predefinite per l'account di servizio Cloud TPU. Se vuoi autorizzazioni finer-grain, esamina le autorizzazioni per il livello di accesso.

La località del bucket deve trovarsi nella stessa regione della macchina virtuale (VM) e del nodo TPU. Le VM e i nodi TPU si trovano in zone specifiche, ovvero suddivisioni all'interno di una regione.

  1. Vai alla pagina Cloud Storage nella console Google Cloud.

    Vai alla pagina Cloud Storage

  2. Crea un nuovo bucket specificando le seguenti opzioni:

    • Un nome univoco a tua scelta.
    • Seleziona Region per Tipo di località e us-central1 per Località (zona)
    • Classe di archiviazione predefinita: Standard
    • Località: specifica una località del bucket nella stessa regione in cui prevedi di creare il tuo nodo TPU. Consulta la pagina relativa ai tipi e alle zone TPU per scoprire dove sono disponibili vari tipi di TPU.

Crea una TPU e una VM

Le risorse TPU sono composte da una macchina virtuale (VM) e da una Cloud TPU con lo stesso nome. Queste risorse devono risiedere nella stessa regione/zona del bucket che hai appena creato.

Puoi configurare le risorse VM e TPU utilizzando i comandi gcloud o tramite Cloud Console. Per ulteriori informazioni sulla gestione delle risorse TPU, vedi Creazione ed eliminazione di TPU.

  1. Apri una finestra di Cloud Shell.

    Apri Cloud Shell

  2. Configura gcloud per utilizzare il progetto.

    $ gcloud config set project your-project
    
    progetto in cui vuoi creare Cloud TPU.

  3. Avvia una VM di Compute Engine e Cloud TPU utilizzando il comando 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
    

    Descrizioni flag comando

    name
    Il nome della Cloud TPU da creare.
    zone
    La zona in cui prevedi di creare la tua Cloud TPU.
    tf-version
    La versione di Tensorflow che il comando gcloud installa sulla tua VM.
    machine-type
    Il tipo di macchina della VM di Compute Engine da creare.
    accelerator-type
    Il tipo di Cloud TPU da creare.

    Per maggiori informazioni sul comando gcloud, consulta la sezione Riferimento gcloud.

  4. Al termine dell'esecuzione del comando gcloud compute tpus execution-groups, verifica che il prompt della shell sia cambiato da username@projectname a username@vm-name. Questa modifica mostra che hai eseguito l'accesso alla tua VM di Compute Engine.

    gcloud compute ssh tpu-name --zone=europe-west4-a
    

Continuando queste istruzioni, esegui ogni comando che inizia con (vm)$ nella finestra della sessione VM.

Installa panda

Installa o esegui l'upgrade dei panda digitando il seguente comando:

pip install pandas

Definisci gli iperparametri

In questa sezione del codice aggiungi diversi iperparametri richiesti dalle TPU. Puoi aggiungere questi iperparametri come flag allo script di addestramento, in modo da poterli modificare in fase di esecuzione.

I parametri aggiunti sono:

  • tpu Questo parametro identifica il nome o l'indirizzo IP del nodo TPU su cui eseguire il modello.
  • modello_dir. Il percorso per salvare i checkpoint del modello. Questo percorso deve essere un bucket Cloud Storage.
  • iterazioni. Il numero di iterazioni per loop di addestramento.
  • usa_tpu. Specifica se vuoi eseguire il modello su TPU o GPU/CPU in base alla disponibilità.

API LTI

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

Caricamento dei dati in corso...

Questa sezione del codice specifica come leggere e caricare i dati.

Le TPU supportano i seguenti tipi di dati:

  • tf.float32
  • tf.complex64
  • tf.int64
  • tf.bool
  • tf.bfloat64

API LTI

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)

Definisci le funzioni di input

Una differenza chiave tra l'API stime e l'API TPUEstimator è la firma della funzione delle funzioni di input. Con l'API Estimator puoi scrivere funzioni di input con un numero qualsiasi di parametri. Con l'API TPUEstimator, le funzioni di input possono assumere un solo parametro, params. Questo params ha tutte le coppie chiave-valore dell'oggetto TPUEstimator, oltre a chiavi aggiuntive come batch_size.

Un modo per risolvere questa differenza è utilizzare le funzioni lambda quando si chiamano le funzioni di input. Con le funzioni lambda, devi apportare solo piccole modifiche alle funzioni di immissione esistenti.

Le sezioni seguenti mostrano come aggiornare le funzioni di immissione. Più avanti, vedrai come utilizzare le funzioni lambda per convertire queste funzioni di input per lavorare con l'API TPUEstimator.

Funzione input di addestramento

Con l'API TPUEstimator, la tua funzione di input per l'addestramento, train_input_fn, deve restituire un numero di campioni di input che possono essere partiti dal numero di core Cloud TPU. Ad esempio, se utilizzi 8 core, ogni dimensione del batch deve essere divisibile per 8.

A questo scopo, il codice precedente utilizza la funzione dataset.batch(batch_size, drop_remainder=True). Questa funzione raggruppa in batch utilizzando il parametro batch_size e ignora il resto.

API LTI

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

Funzione di input di valutazione

In questo passaggio, aggiorni la funzione di input di valutazione, eval_input_fn, per garantire che i campioni di input possano essere suddivisi in partizionamenti in base al numero di core TPU. A tale scopo, utilizza la funzione dataset.batch(batch_size, drop_remainder=True).

API LTI

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

Funzione di input della previsione

Per le previsioni in TPUEstimator, il set di dati di input deve avere tensori con la dimensione esterna aggiuntiva di batch_size. Di conseguenza, devi aggiungere una funzione di input di previsione, che utilizza features e batch_size come parametri. Questa funzione consente di avere meno campioni di input rispetto a batch_size.

La funzione di input di previsione è facoltativa se utilizzi l'API LTI.

API LTI

Una funzione di input di previsione è facoltativa per l'API stime, perché la funzione di valutazione eval_input_fn esegue questa attività.

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

Aggiorna la funzione del modello personalizzato

L'attività successiva consiste nell'aggiornare la funzione del modello personalizzato:

  • Sostituisci le istanze di tf.estimator.EstimatorSpec per utilizzare tf.contrib.tpu.TPUEstimatorSpec.
  • Rimuovi eventuali istanze di tf.summary. L'API TPUEstimator non supporta i riepiloghi personalizzati per tensorboard. Tuttavia, i riepiloghi di base vengono registrati automaticamente nei file evento della directory dei modelli.
  • Esegui il wrapping dello strumento di ottimizzazione con tf.contrib.tpu.CrossShardOptimizer. CrossShardOptimizer utilizza un allreduce per aggregare i gradienti e trasmettere il risultato a ogni shard. CrossShardOptimizer non è compatibile con l'addestramento locale, quindi devi controllare anche il flag use_tpu.

API LTI

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)

Aggiungi una funzione di metrica della valutazione

Un'altra differenza tra l'API stime e l'API TPUEstimator è il modo in cui gestiscono le metriche. Con l'API LTI, puoi trasmettere le metriche come un normale dizionario. Per l'API TPUEstimator devi invece utilizzare una funzione.

API LTI

Campo facoltativo. La funzione my_model genera le metriche.

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}

Aggiornare la funzione principale

Configura TPU

In questo passaggio devi configurare il cluster TPU.

Per configurare il cluster, puoi utilizzare i valori assegnati agli iperparametri. Per ulteriori informazioni, consulta la sezione Definire gli iperparametri. Inoltre, devi impostare i seguenti valori:

  • allow_soft_placement. Se impostato su "true", questo parametro consente a TensorFlow di utilizzare un dispositivo GPU se una TPU non è disponibile. Se non è disponibile un dispositivo GPU, viene utilizzato anche un dispositivo CPU.
  • log_device_placement. Indica che TensorFlow deve registrare i posizionamenti dei dispositivi.

API LTI

Non obbligatorio, perché questa sezione di codice riguarda solo le 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),
)

Aggiungere parametri specifici di TPU alla categoria di classificazione

In questa sezione del codice, aggiorni la variabile classificatore in modo che utilizzi la classe TPUEstimator. Questa modifica richiede l'aggiunta dei seguenti parametri:

  • use_tpu
  • train_batch_size
  • eval_batch_size
  • predict_batch_size
  • config

API LTI

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

Chiama il metodo del treno

La modifica successiva consiste nell'aggiornare il metodo del treno. Nota l'utilizzo di una funzione lambda per chiamare la funzione train_input_fn. Questo metodo semplifica l'uso delle funzioni esistenti con l'API TPUEstimator.

Inoltre, devi modificare il parametro dei passaggi in max_steps. Nella sezione successiva, riproponi il parametro Passaggi per specificare il numero di passaggi di valutazione.

API LTI

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

Chiama il metodo di valutazione

Questa modifica è simile a quella apportata al metodo del treno. Anche in questo caso, l'uso di una funzione lambda rende più semplice l'utilizzo di una funzione di input di valutazione esistente.

Inoltre, devi modificare il parametro steps con il valore impostato dal flag della riga di comando eval_steps.

API LTI

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

Chiama il metodo di previsione

Come per il metodo Treno e Valutazione, devi aggiornare il metodo di previsione. Anche in questo caso, l'uso di una funzione lambda semplifica l'uso di una funzione di input di valutazione esistente.

API LTI

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

Esegui la pulizia

Per evitare che al tuo account GCP vengano addebitati costi relativi alle risorse utilizzate in questo argomento:

  1. Disconnettiti dalla VM di Compute Engine:

    (vm)$ exit
    

    Il prompt ora dovrebbe essere username@projectname, a indicare che ti trovi in Cloud Shell.

  2. In Cloud Shell, esegui ctpu delete con il flag --zone che hai utilizzato durante la configurazione di Cloud TPU per eliminare la VM di Compute Engine e la tua Cloud TPU:

    $ ctpu delete [optional: --zone]
    
  3. Esegui ctpu status per assicurarti di non avere istanze allocate per evitare addebiti non necessari per l'utilizzo di TPU. L'eliminazione può richiedere qualche minuto. Una risposta come quella riportata di seguito indica che non sono presenti altre istanze allocate:

    $ 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:             --
    
  4. Esegui gsutil come mostrato, sostituendo bucket-name con il nome del bucket Cloud Storage che hai creato per questo tutorial:

    $ gsutil rm -r gs://bucket-name
    

Passaggi successivi

Per scoprire di più sulle API stime e TPUEstimator, consulta i seguenti argomenti: