Introduzione al convertitore di inferenza Cloud TPU v5e [anteprima pubblica]

Introduzione

Cloud TPU Inference Converter prepara e ottimizza un modello TensorFlow 2 (TF2) per l'inferenza TPU. Il convertitore viene eseguito in una shell VM locale o TPU. La shell della VM TPU è consigliata perché è preinstallata con gli strumenti a riga di comando necessari per il convertitore. prende un SalvaModel esportato ed esegue i seguenti passaggi:

  1. Conversione TPU: aggiunge TPUPartitionedCall e altre operazioni TPU al modello per renderlo pubblicabile sulla TPU. Per impostazione predefinita, un modello esportato per l'inferenza non dispone di queste operazioni e non può essere pubblicato sulla TPU, anche se è stato addestrato sulla TPU.
  2. Raggruppamento: aggiunge operazioni di batch al modello per abilitare la creazione in batch nel grafico e migliorare la velocità effettiva.
  3. Conversione BFloat16: converte il formato dei dati del modello da float32 a bfloat16 per migliorare le prestazioni di calcolo e ridurre l'utilizzo della memoria HBM (High Bandwidth Memory) sulla TPU.
  4. Ottimizzazione forma di I/O: ottimizza le forme dei tensori per i dati trasferiti tra CPU e TPU per migliorare l'utilizzo della larghezza di banda.

Durante l'esportazione di un modello, gli utenti creano alias di funzione per tutte le funzioni che vogliono eseguire sulla TPU. Passano queste funzioni al convertitore che, a sua volta, le posiziona nella TPU e le ottimizza. Se queste funzioni contengono operazioni non compatibili con TPU, è possibile abilitare il posizionamento del soft device in modo che le operazioni incompatibili vengano eseguite sulla CPU.

Il convertitore di inferenza Cloud TPU è disponibile come immagine Docker che può essere eseguita in qualsiasi ambiente in cui è installato Docker.

Tempo stimato per completare i passaggi mostrati sopra: ~20 min - 30 min

Prerequisiti

  1. Il modello deve essere un modello TF2 ed esportato nel formato SaveModel.
  2. Il modello deve avere un alias di funzione per la funzione TPU. Per informazioni su come eseguire questa operazione, consulta l'esempio di codice. Negli esempi seguenti viene utilizzato tpu_func come alias della funzione TPU.
  3. Assicurati che la CPU della tua macchina supporti le istruzioni AVX (Advanced Vector eXtensions), poiché la libreria Tensorflow (la dipendenza del convertitore di inferenza Cloud TPU) viene compilata per utilizzare le istruzioni AVX. La maggior parte delle CPU supporta AVX.
    1. Puoi eseguire lscpu | grep avx per verificare se il set di istruzioni AVX è supportato.

Prima di iniziare

Prima di iniziare la configurazione:

  • Crea un nuovo progetto: nella console Google Cloud, nella pagina del selettore dei progetti, seleziona o crea un progetto Cloud.

  • Configura una VM Cloud TPU: crea una nuova VM Cloud TPU utilizzando la console Google Cloud o gcloud oppure utilizza una VM Cloud TPU esistente per eseguire l'inferenza con il modello convertito sulla VM Cloud TPU.

    • Assicurati che l'immagine VM Cloud TPU sia basata su Tensorflow. Ad esempio, --version=tpu-vm-tf-2.11.0.
    • Il modello convertito verrà caricato e gestito su questa VM Cloud TPU.
  • Assicurati di disporre degli strumenti a riga di comando necessari per utilizzare il convertitore di inferenza Cloud TPU. Puoi installare Google Cloud SDK e Docker localmente oppure utilizzare una VM Cloud TPU in cui questo software è installato per impostazione predefinita. Utilizza questi strumenti per interagire con l'immagine del convertitore.

    Puoi utilizzare SSH nella VM Cloud TPU utilizzando il seguente comando:

    gcloud compute tpus tpu-vm ssh ${tpu-name} --zone ${zone} --project ${project-id}
    

Configurazione dell'ambiente

Configura l'ambiente dalla shell della VM TPU o dalla shell locale.

Shell VM TPU

  • Nella shell della VM TPU, esegui i comandi seguenti per consentire l'utilizzo di Docker non root:

    sudo usermod -a -G docker ${USER}
    newgrp docker
    
  • Inizializza gli helper delle credenziali Docker:

    gcloud auth configure-docker \
      us-docker.pkg.dev
    

Shell locale

Nella shell locale, configura l'ambiente seguendo questi passaggi:

  • Installare Cloud SDK, che include lo strumento a riga di comando gcloud.

  • Installa Docker:

  • Consenti l'utilizzo di Docker non root:

    sudo usermod -a -G docker ${USER}
    newgrp docker
    
  • Accedi al tuo ambiente:

    gcloud auth login
    
  • Inizializza gli helper delle credenziali Docker:

    gcloud auth configure-docker \
        us-docker.pkg.dev
    
  • Esegui il pull dell'immagine Docker del convertitore di inferenza:

      CONVERTER_IMAGE=us-docker.pkg.dev/cloud-tpu-images/inference/tpu-inference-converter-cli:2.13.0
      docker pull ${CONVERTER_IMAGE}
      

Immagine convertitore

L'immagine è utilizzata per le conversioni del modello una tantum. Imposta i percorsi dei modelli e modifica le opzioni del convertitore in base alle tue esigenze. La sezione Esempi di utilizzo fornisce diversi casi d'uso comuni.

docker run \
--mount type=bind,source=${MODEL_PATH},target=/tmp/input,readonly \
--mount type=bind,source=${CONVERTED_MODEL_PATH},target=/tmp/output \
${CONVERTER_IMAGE} \
--input_model_dir=/tmp/input \
--output_model_dir=/tmp/output \
--converter_options_string='
    tpu_functions {
      function_alias: "tpu_func"
    }
    batch_options {
      num_batch_threads: 2
      max_batch_size: 8
      batch_timeout_micros: 5000
      allowed_batch_sizes: 2
      allowed_batch_sizes: 4
      allowed_batch_sizes: 8
      max_enqueued_batches: 10
    }
'

Inferenza con il modello convertito nella VM Cloud TPU

# Initialize the TPU
resolver = tf.distribute.cluster_resolver.TPUClusterResolver("local")
tf.config.experimental_connect_to_cluster(resolver)
tf.tpu.experimental.initialize_tpu_system(resolver)

# Load the model
model = tf.saved_model.load(${CONVERTED_MODEL_PATH})

# Find the signature function for serving
serving_signature = 'serving_default' # Change the serving signature if needed
serving_fn = model.signatures[serving_signature]
# Run the inference using requests.
results = serving_fn(**inputs)
logging.info("Serving results: %s", str(results))

Esempi di utilizzo

Aggiungi un alias di funzione per la funzione TPU

  1. Trova o crea una funzione nel modello che esegua il wrapping di tutto ciò che vuoi eseguire sulla TPU. Se @tf.function non esiste, aggiungilo.
  2. Quando salvi il modello, fornisci SaveOptions come indicato di seguito per assegnare a model.tpu_func un alias func_on_tpu.
  3. Puoi passare l'alias di questa funzione all'utente che ha completato una conversione.
class ToyModel(tf.keras.Model):
  @tf.function(
      input_signature=[tf.TensorSpec(shape=[None, 10], dtype=tf.float32)])
  def tpu_func(self, x):
    return x * 1.0

model = ToyModel()
save_options = tf.saved_model.SaveOptions(function_aliases={
    'func_on_tpu': model.tpu_func,
})
tf.saved_model.save(model, model_dir, options=save_options)

Converti un modello con più funzioni TPU

Puoi inserire più funzioni nella TPU. È sufficiente creare più alias di funzione e trasmetterli in converter_options_string all'utente che ha completato la conversione.

tpu_functions {
  function_alias: "tpu_func_1"
}
tpu_functions {
  function_alias: "tpu_func_2"
}

Quantizzazione

La quantizzazione è una tecnica che riduce la precisione dei numeri utilizzati per rappresentare i parametri di un modello. Ciò si traduce in un modello di dimensioni inferiori e in un calcolo più rapido. Un modello quantizzato consente di aumentare la velocità effettiva di inferenza, nonché di ridurre l'utilizzo della memoria e le dimensioni di archiviazione, a scapito di piccoli cali di accuratezza.

La nuova funzionalità di quantizzazione post-addestramento in TensorFlow che ha come target TPU è sviluppata a partire dalla funzionalità esistente simile di TensorFlow Lite che viene utilizzata per il targeting di dispositivi mobili e periferici. Per ulteriori informazioni sulla quantizzazione in generale, puoi dare un'occhiata al documento di TensorFlow Lite.

Concetti sulla quantizzazione

Questa sezione definisce i concetti specificamente correlati alla quantizzazione con il convertitore di inferenza.

I concetti relativi ad altre configurazioni di TPU (ad esempio sezioni, host, chip e TensorCore) sono descritti nella pagina Architettura del sistema TPU.

  • Quantizzazione post-addestramento (PTQ): è una tecnica che riduce le dimensioni e la complessità computazionale di un modello di rete neurale senza influenzarne significativamente la precisione. Il PTQ converte le ponderazioni e le attivazioni di un modello addestrato in numeri interi a precisione inferiore, ad esempio numeri interi a 8 bit o 16 bit. Questo può causare una riduzione significativa delle dimensioni del modello e della latenza di inferenza, causando solo una piccola perdita in accuratezza.

  • Calibrazione: la fase di calibrazione per la quantizzazione è il processo di raccolta di statistiche sull'intervallo di valori assunti dalle ponderazioni e dalle attivazioni di un modello di rete neurale. Queste informazioni vengono utilizzate per determinare i parametri di quantizzazione per il modello, che sono i valori che verranno utilizzati per convertire le ponderazioni e le attivazioni a virgola mobile in numeri interi.

  • Set di dati rappresentativo: un set di dati rappresentativo per la quantizzazione è un piccolo set di dati che rappresenta i dati di input effettivi per il modello. Viene utilizzato durante la fase di calibrazione della quantizzazione per raccogliere statistiche sull'intervallo di valori che verranno assunti dalle ponderazioni e dalle attivazioni del modello. Il set di dati rappresentativo deve soddisfare le seguenti proprietà:

    • Deve rappresentare correttamente gli input effettivi per il modello durante l'inferenza. Ciò significa che deve coprire l'intervallo di valori che è probabile che il modello veda nel mondo reale.
    • Dovrebbero fluire collettivamente attraverso ogni ramo di condizionali (ad esempio tf.cond), se presenti. Questo è importante perché il processo di quantizzazione deve essere in grado di gestire tutti i possibili input del modello, anche se non sono esplicitamente rappresentati nel set di dati rappresentativo.
    • Dovrebbe essere abbastanza grande da raccogliere statistiche sufficienti e ridurre gli errori. Come regola generale, consigliamo di utilizzare più di 200 campioni rappresentativi.

    Il set di dati rappresentativo può essere un sottoinsieme del set di dati di addestramento oppure un set di dati separato progettato specificamente per rappresentare gli input reali del modello. La scelta del set di dati da utilizzare dipende dall'applicazione specifica.

  • Static Range Quantization (SRQ): SRQ determina l'intervallo di valori per le ponderazioni e le attivazioni di un modello di rete neurale una sola volta, durante la fase di calibrazione. Ciò significa che lo stesso intervallo di valori viene utilizzato per tutti gli input del modello. Questo può essere meno preciso della quantizzazione dell'intervallo dinamico, soprattutto per i modelli con un'ampia gamma di valori di input. Tuttavia, la quantizzazione dell'intervallo statico richiede meno calcoli in fase di runtime rispetto alla quantizzazione dell'intervallo dinamico.

  • Dynamic Range Quantization (DRQ): DRQ determina l'intervallo di valori per le ponderazioni e le attivazioni di un modello di rete neurale per ogni input. Ciò consente al modello di adattarsi all'intervallo di valori dei dati di input, il che può migliorare l'accuratezza. Tuttavia, la quantizzazione dell'intervallo dinamico richiede un maggiore calcolo in fase di esecuzione rispetto alla quantizzazione dell'intervallo statico.

    Funzionalità Quantizzazione dell'intervallo statico Quantizzazione dell'intervallo dinamico
    Intervallo di valori Determinato una volta, durante la calibrazione Determinato per ogni input
    Accuratezza Può essere meno preciso, soprattutto per i modelli con un'ampia gamma di valori di input Può essere più preciso, soprattutto per i modelli con un'ampia gamma di valori di input
    complessità Più semplice Più complesso
    Calcolo in fase di esecuzione Meno operazioni di calcolo Più calcolo
  • Quantizzazione solo pesi: la quantizzazione di sole ponderazioni è un tipo di quantizzazione che quantifica solo le ponderazioni di un modello di rete neurale, lasciando le attivazioni in virgola mobile. Questa può essere una buona opzione per i modelli sensibili all'accuratezza, in quanto può aiutare a preservare l'accuratezza del modello.

Come utilizzare la quantizzazione

La quantizzazione può essere applicata configurando e impostando QuantizationOptions per le opzioni del convertitore. Le opzioni degne di nota sono:

  • tag: raccolta di tag che identificano il MetaGraphDef all'interno di SavedModel da quantificare. Non è necessario specificare un solo valore MetaGraphDef.
  • firma_keys: sequenza di chiavi che identificano SignatureDef contenenti input e output. Se non specificato, viene utilizzato ["serving_default"].
  • quantization_method: metodo di quantizzazione da applicare. Se non specificato, verrà applicata la quantizzazione STATIC_RANGE.
  • op_set: deve essere mantenuto come XLA. Attualmente è l'opzione predefinita, non è necessario specificarla.
  • rappresentativo_set di dati: specifica il set di dati utilizzato per calibrare i parametri di quantizzazione.

Creazione del set di dati rappresentativo

Un set di dati rappresentativo è essenzialmente un iterabile di campioni. Dove un campione è una mappa di: {input_key: input_value}. Ad esempio:

representative_dataset = [{"x": tf.random.uniform(shape=(3, 3))}
                          for _ in range(256)]

I set di dati rappresentativi devono essere salvati come file TFRecord utilizzando la classe TfRecordRepresentativeDatasetSaver attualmente disponibile nel pacchetto tf-nightly pip. Ad esempio:

# Assumed tf-nightly installed.
import tensorflow as tf
representative_dataset = [{"x": tf.random.uniform(shape=(3, 3))}
                          for _ in range(256)]
tf.quantization.experimental.TfRecordRepresentativeDatasetSaver(
       path_map={'serving_default': '/tmp/representative_dataset_path'}
    ).save({'serving_default': representative_dataset})

Esempi

L'esempio seguente quantizza il modello con la chiave di firma di serving_default e l'alias funzione di tpu_func:

docker run \
  --mount type=bind,source=${MODEL_PATH},target=/tmp/input,readonly \
  --mount type=bind,source=${CONVERTED_MODEL_PATH},target=/tmp/output \
  ${CONVERTER_IMAGE} \
  --input_model_dir=/tmp/input \
  --output_model_dir=/tmp/output \
  --converter_options_string=' \
    tpu_functions { \
      function_alias: "tpu_func" \
    } \
    external_feature_configs { \
      quantization_options { \
        signature_keys: "serving_default" \
        representative_datasets: { \
          key: "serving_default" \
          value: { \
            tfrecord_file_path: "${TF_RECORD_FILE}" \
          } \
        } \
      } \
    } '

Abilita posizionamento soft device

Il posizionamento dei soft device può spostare le operazioni non compatibili con TPU da eseguire sulla CPU in fase di runtime. Se all'interno della funzione TPU il modello ha un operatore non compatibile con TPU che non può essere separato facilmente, puoi abilitare il posizionamento del soft device impostando enable_soft_device_placement=True sulla funzione TPU. Questo flag indica al runtime di verificare la presenza di operazioni non compatibili con TPU ed eseguirle sulla CPU.

Il posizionamento del soft device è utile anche per il debug. Con questa funzione puoi utilizzare tf.print, che non è compatibile con TPU, all'interno della funzione TPU per stampare i valori dei tensori.

Il posizionamento dei soft device funziona al meglio con il bridge MLIR. È necessario impostare un flag aggiuntivo.

tpu_functions {
  function_alias: "tpu_func"
  enable_soft_device_placement: true
}

Aggiungi raggruppamento

Il convertitore può essere utilizzato per aggiungere batch a un modello. Per una descrizione delle opzioni di batch che possono essere ottimizzate, consulta la Definizione delle opzioni di batch.

Per impostazione predefinita, il convertitore raggruppa tutte le funzioni TPU nel modello. Può anche eseguire il batch di firme e funzioni fornite dall'utente, che possono migliorare ulteriormente le prestazioni. Qualsiasi funzione TPU, firma fornita dall'utente o firma in batch deve soddisfare i requisiti di forma rigida dell'operazione di raggruppamento.

Il convertitore può anche aggiornare le opzioni di raggruppamento in batch esistenti. Di seguito è riportato un esempio di come aggiungere batch a un modello. Per ulteriori informazioni sul raggruppamento, consulta l'articolo Approfondimento sul batch.

batch_options {
  num_batch_threads: 2
  max_batch_size: 8
  batch_timeout_micros: 5000
  allowed_batch_sizes: 2
  allowed_batch_sizes: 4
  allowed_batch_sizes: 8
  max_enqueued_batches: 10
}

Disattiva le ottimizzazioni di bfloat16 e della forma di ordine di inserzione

Le ottimizzazioni delle forme BFloat16 e IO sono abilitate per impostazione predefinita. Se non funzionano bene con il tuo modello, possono essere disabilitati.

# Disable both optimizations
disable_default_optimizations: true

# Or disable them individually
io_shape_optimization: DISABLED
bfloat16_optimization: DISABLED

Report sulle conversioni

Puoi trovare questo report di conversione nel log dopo aver eseguito il convertitore di inferenza. Di seguito è riportato un esempio.

-------- Conversion Report --------
TPU cost of the model: 96.67% (2034/2104)
CPU cost of the model:  3.33% (70/2104)

Cost breakdown
================================
%         Cost    Name
--------------------------------
3.33      70      [CPU cost]
48.34     1017    tpu_func_1
48.34     1017    tpu_func_2
--------------------------------

Questo report stima il costo di calcolo del modello di output su CPU e TPU e suddivide ulteriormente il costo delle TPU per ogni funzione, il che dovrebbe riflettere la tua selezione delle funzioni TPU nelle opzioni del convertitore.

Se vuoi utilizzare meglio la TPU, ti consigliamo di sperimentare la struttura del modello e modificare le opzioni del convertitore.

Domande frequenti

Quali funzioni devo inserire sulla TPU?

È meglio applicare il maggior numero possibile di modelli sulla TPU, poiché la maggior parte delle operazioni viene eseguita più velocemente sulla TPU.

Se il modello non contiene operazioni, stringhe o tensori sparsi non compatibili con TPU, l'implementazione dell'intero modello sulla TPU è in genere la strategia migliore. Puoi farlo trovando o creando una funzione che aggreghi l'intero modello, creando un alias di funzione e passandolo al convertitore.

Se il modello contiene parti che non possono funzionare sulla TPU (ad esempio operazioni, stringhe o tensori sparsi non compatibili con TPU), la scelta delle funzioni TPU dipende da dove si trova la parte incompatibile.

  • Se il modello è all'inizio o alla fine, puoi eseguire il refactoring del modello per mantenerlo sulla CPU. Esempi sono le fasi di pre e post-elaborazione delle stringhe. Per saperne di più sullo spostamento del codice sulla CPU, consulta la sezione "Come posso spostare una parte del modello sulla CPU?" Mostra un modo tipico di refactoring del modello.
  • Se si trova a metà del modello ed è solo poche operazioni, il posizionamento del dispositivo soft può essere preso in considerazione. Tuttavia, può comportare una latenza di inferenza maggiore a causa delle comunicazioni aggiuntive tra CPU e TPU.
  • Se si trova a metà del modello e le operazioni sono numerose, il posizionamento del dispositivo flessibile renderebbe l'inferenza troppo costosa. Quindi è meglio suddividere il modello in tre parti e contenere tutte le operazioni non compatibili con TPU nella parte centrale ed eseguirlo sulla CPU.
  • Se si tratta di un tensore sparso, potresti chiamare tf.sparse.to_dense sulla CPU e passare il tensore denso risultante alla parte TPU del modello.

Un altro fattore da considerare è l'utilizzo di HBM, L'incorporamento delle tabelle può utilizzare molta HBM. Se si espandono oltre i limiti hardware della TPU, devono essere inserite nella CPU, insieme alle operazioni di ricerca.

Quando possibile, deve esistere una sola funzione TPU sotto una firma. Se la struttura del modello richiede la chiamata di più funzioni TPU per ogni richiesta di inferenza in arrivo, è necessario essere consapevoli della maggiore latenza dell'invio dei tensori tra CPU e TPU.

Un buon modo per valutare la selezione delle funzioni TPU è controllare il report sulle conversioni. Mostra la percentuale di calcolo eseguita sulla TPU e un'analisi dettagliata del costo di ogni funzione TPU.

Come posso spostare una parte del modello sulla CPU?

Se il modello contiene parti che non possono essere gestite sulla TPU, devi refactoring del modello per spostarle nella CPU. Ecco un esempio di giocattolo. Il modello è un modello linguistico con una fase di pre-elaborazione. Il codice per le definizioni e le funzioni dei livelli viene omesso per semplicità.

class LanguageModel(tf.keras.Model):
  @tf.function
  def model_func(self, input_string):
    word_ids = self.preprocess(input_string)
    return self.bert_layer(word_ids)

Questo modello non può essere pubblicato direttamente sulla TPU per due motivi. Il primo è una stringa. In secondo luogo, la funzione preprocess può contenere molte operazioni di stringa. Entrambe non sono compatibili con TPU.

Per il refactoring di questo modello, puoi creare un'altra funzione chiamata tpu_func per ospitare bert_layer. Quindi crea un alias di funzione per tpu_func e passalo al convertitore. In questo modo, tutti i contenuti all'interno di tpu_func verranno eseguiti sulla TPU, mentre tutto quello che rimane in model_func verrà eseguito sulla CPU.

class LanguageModel(tf.keras.Model):
  @tf.function
  def tpu_func(self, word_ids):
    return self.bert_layer(word_ids)

  @tf.function
  def model_func(self, input_string):
    word_ids = self.preprocess(input_string)
    return self.tpu_func(word_ids)

Cosa devo fare se il modello ha operazioni, stringhe o tensori sparsi non compatibili con TPU?

La maggior parte delle operazioni TensorFlow standard è supportata sulla TPU, ma alcune, tra cui stringhe e tensori sparsi, non sono supportate. Il convertitore non controlla la presenza di operazioni non compatibili con TPU. Pertanto, un modello che contiene queste operazioni può trasmettere la conversione. Tuttavia, quando viene eseguito per l'inferenza, si verificano errori come quelli riportati di seguito.

'tf.StringToNumber' op isn't compilable for TPU device. enable soft_device_placement option to run on CPU

Se il modello ha operazioni non compatibili con TPU, queste devono essere esterne alla funzione TPU. Inoltre, la stringa è un formato dati non supportato sulla TPU. Pertanto, le variabili di tipo stringa non devono essere inserite nella funzione TPU. Anche i parametri e i valori restituiti della funzione TPU non devono essere di tipo stringa. Allo stesso modo, evita di inserire tensori sparsi nella funzione TPU, inclusi i parametri e i valori restituiti.

Solitamente non è difficile eseguire il refactoring della parte incompatibile del modello e spostarla sulla CPU. Ecco un esempio: Tuttavia, in alcuni casi le operazioni non compatibili con TPU sono profondamente nidificate nel modello e sono difficili da separare. Per questi modelli, puoi prendere in considerazione l'abilitazione del posizionamento dei soft device per eseguire le operazioni sulla CPU in fase di runtime. Tuttavia, tieni presente l'impatto sulle prestazioni.

Come supportare operazioni personalizzate nel modello?

Se nel modello vengono utilizzate operazioni personalizzate, il convertitore potrebbe non riconoscerle e non riuscire a convertire il modello. Questo perché la libreria op dell'operazione personalizzata, che contiene la definizione completa dell'operazione, non è collegata al convertitore.

Poiché al momento il codice del convertitore non è ancora open source, non può essere creato con un'operazione personalizzata.

Cosa devo fare se ho un modello TensorFlow 1?

Il convertitore non supporta i modelli TensorFlow 1. È necessario eseguire la migrazione dei modelli TensorFlow 1 a TensorFlow 2.

Devo abilitare il bridge MLIR quando eseguo il mio modello?

La maggior parte dei modelli convertiti può essere eseguita con il più recente bridge TF2XLA MLIR o con il bridge TF2XLA originale.

Durante l'addestramento e l'inferenza, il posizionamento del soft device in TensorFlow 2 funziona al meglio con il bridge MLIR. Il bridge MLIR può essere abilitato utilizzando queste istruzioni.

Come posso convertire un modello già esportato senza un alias di funzione?

Se un modello è stato esportato senza un alias di funzione, il modo più semplice consiste nel esportarlo di nuovo e creare un alias di funzione. Se la riesportazione non è possibile, è comunque possibile convertire il modello fornendo un valore concrete_function_name. Tuttavia, identificare il concrete_function_name corretto richiede un certo lavoro di indagine.

Gli alias di funzione sono una mappatura da una stringa definita dall'utente a un nome concreto di funzione. Rendono più semplice fare riferimento a una funzione specifica nel modello. Il convertitore accetta sia alias di funzione sia nomi di funzioni concrete non elaborati.

Puoi trovare i nomi concreti delle funzioni esaminando saved_model.pb.

L'esempio seguente mostra come inserire una funzione concreta denominata __inference_serve_24 sulla TPU.

sudo docker run \
--mount type=bind,source=${MODEL_PATH},target=/tmp/input,readonly \
--mount type=bind,source=${CONVERTED_MODEL_PATH},target=/tmp/output \
${CONVERTER_IMAGE} \
--input_model_dir=/tmp/input \
--output_model_dir=/tmp/output \
--converter_options_string='
    tpu_functions {
      concrete_function_name: "__inference_serve_24"
    }'

Come posso risolvere un errore di vincolo della costante di tempo di compilazione?

Sia per l'addestramento che per l'inferenza, XLA richiede che gli input per determinate operazioni abbiano una forma nota al momento della compilazione delle TPU. Ciò significa che quando XLA compila la porzione TPU del programma, gli input per queste operazioni devono avere una forma nota staticamente.

Esistono due modi per risolvere il problema.

  • La soluzione migliore è aggiornare gli input dell'operazione in modo che abbiano una forma nota staticamente quando XLA compila il programma TPU. Questa compilazione avviene immediatamente prima dell'esecuzione della parte TPU del modello. Ciò significa che la forma dovrebbe essere nota in modo statico quando TpuFunction sta per essere eseguito.
  • Un'altra opzione è modificare TpuFunction in modo da non includere più l'operazione problematica.

Perché viene visualizzato un errore di raggruppamento delle forme?

Il raggruppamento in batch ha requisiti di forma rigorosi che consentono di raggruppare le richieste in entrata lungo la 0a dimensione (nota anche come dimensione di batch). Questi requisiti di forma derivano dall'operazione di raggruppamento di TensorFlow e non possono essere rilassati.

Il mancato rispetto di questi requisiti causerà errori quali:

  1. I tensori di input del raggruppamento devono avere almeno una dimensione.
  2. Le dimensioni degli input devono corrispondere.
  3. I tensori di input di raggruppamento forniti in una determinata chiamata di un'operazione devono avere la stessa dimensione della dimensione 0.
  4. La dimensione 0 del tensore di output in batch non corrisponde alla somma delle dimensioni della 0a dimensione dei tensori di input.

Per soddisfare questi requisiti, valuta la possibilità di fornire una funzione o una firma diversa per il batch. Potrebbe anche essere necessario modificare le funzioni esistenti per soddisfare questi requisiti.

Se una funzione è in fase di raggruppamento, assicurati che le forme input_signature di @tf.function abbiano tutte il valore Nessuno nella 0a dimensione. Se una firma viene creata in batch, assicurati che tutti i suoi input abbiano -1 nella 0a dimensione.

Per una spiegazione completa sul motivo per cui si verificano questi errori e su come risolverli, consulta la sezione Analisi dettagliata dei batch.

Problemi noti

La funzione TPU non può chiamare indirettamente un'altra funzione TPU

Sebbene il convertitore sia in grado di gestire la maggior parte degli scenari di chiamata delle funzioni attraverso il confine CPU-TPU, può verificarsi un raro caso limite. Si verifica quando una funzione TPU chiama indirettamente un'altra funzione TPU.

Questo perché il convertitore modifica il chiamante diretto di una funzione TPU dalla chiamata della funzione TPU stessa alla chiamata di uno stub di chiamata TPU. Lo stub di chiamata contiene operazioni che possono funzionare solo sulla CPU. Quando una funzione TPU chiama qualsiasi funzione che alla fine chiama il chiamante diretto, le operazioni della CPU potrebbero essere portate sulla TPU per l'esecuzione, il che genererà errori del kernel mancanti. Tieni presente che questo caso è diverso da una funzione TPU che chiama direttamente un'altra funzione TPU. In questo caso, il convertitore non modifica nessuna funzione per richiamare lo stub di chiamata, quindi può funzionare.

Nel convertitore abbiamo implementato il rilevamento di questo scenario. Se viene visualizzato il seguente errore, significa che il modello ha riscontrato questo caso limite:

Unable to place both "__inference_tpu_func_2_46" and "__inference_tpu_func_4_68"
on the TPU because "__inference_tpu_func_2_46" indirectly calls
"__inference_tpu_func_4_68". This behavior is unsupported because it can cause
invalid graphs to be generated.

La soluzione generale consiste nel refactoring del modello per evitare questo scenario di chiamate di funzioni. Se lo ritieni difficile, contatta il team dell'Assistenza Google per discuterne più approfonditamente.

Riferimento

Opzioni del convertitore in formato Protobuf

message ConverterOptions {
  // TPU conversion options.
  repeated TpuFunction tpu_functions = 1;

  // The state of an optimization.
  enum State {
    // When state is set to default, the optimization will perform its
    // default behavior. For some optimizations this is disabled and for others
    // it is enabled. To check a specific optimization, read the optimization's
    // description.
    DEFAULT = 0;
    // Enabled.
    ENABLED = 1;
    // Disabled.
    DISABLED = 2;
  }

  // Batch options to apply to the TPU Subgraph.
  //
  // At the moment, only one batch option is supported. This field will be
  // expanded to support batching on a per function and/or per signature basis.
  //
  //
  // If not specified, no batching will be done.
  repeated BatchOptions batch_options = 100;

  // Global flag to disable all optimizations that are enabled by default.
  // When enabled, all optimizations that run by default are disabled. If a
  // default optimization is explicitly enabled, this flag will have no affect
  // on that optimization.
  //
  // This flag defaults to false.
  bool disable_default_optimizations = 202;

  // If enabled, apply an optimization that reshapes the tensors going into
  // and out of the TPU. This reshape operation improves performance by reducing
  // the transfer time to and from the TPU.
  //
  // This optimization is incompatible with input_shape_opt which is disabled.
  // by default. If input_shape_opt is enabled, this option should be
  // disabled.
  //
  // This optimization defaults to enabled.
  State io_shape_optimization = 200;

  // If enabled, apply an optimization that updates float variables and float
  // ops on the TPU to bfloat16. This optimization improves performance and
  // throughtput by reducing HBM usage and taking advantage of TPU support for
  // bfloat16.
  //
  // This optimization may cause a loss of accuracy for some models. If an
  // unacceptable loss of accuracy is detected, disable this optimization.
  //
  // This optimization defaults to enabled.
  State bfloat16_optimization = 201;

  BFloat16OptimizationOptions bfloat16_optimization_options = 203;

  // The settings for XLA sharding. If set, XLA sharding is enabled.
  XlaShardingOptions xla_sharding_options = 204;
}

message TpuFunction {
  // The function(s) that should be placed on the TPU. Only provide a given
  // function once. Duplicates will result in errors. For example, if
  // you provide a specific function using function_alias do not also provide the
  // same function via concrete_function_name or jit_compile_functions.
  oneof name {
    // The name of the function alias associated with the function that
    // should be placed on the TPU. Function aliases are created during model
    // export using the tf.saved_model.SaveOptions.
    //
    // This is a recommended way to specify which function should be placed
    // on the TPU.
    string function_alias = 1;

    // The name of the concrete function that should be placed on the TPU. This
    // is the name of the function as it found in the GraphDef and the
    // FunctionDefLibrary.
    //
    // This is NOT the recommended way to specify which function should be
    // placed on the TPU because concrete function names change every time a
    // model is exported.
    string concrete_function_name = 3;

    // The name of the signature to be placed on the TPU. The user must make
    // sure there is no TPU-incompatible op under the entire signature. Or soft
    // device placement can be enabled. But this will incur performance loss.
    string signature_name = 5;

    // When jit_compile_functions is set to True, all jit compiled functions
    // are placed on the TPU.
    //
    // To use this option, decorate the relevant function(s) with
    // @tf.function(jit_compile=True), before exporting. Then set this flag to
    // True. The converter will find all functions that were tagged with
    // jit_compile=True and place them on the TPU.
    //
    // When using this option, all other settings for the TpuFunction such as
    // enable_soft_device_placement will apply to all functions tagged with
    // jit_compile=True.
    //
    // This option will place all jit_compile=True functions on the TPU.
    // If only some jit_compile=True functions should be placed on the TPU,
    // use function_alias or concrete_function_name.
    bool jit_compile_functions = 4;
  }

  // Set to true to enable outside compilation for this TPU function. If the TPU
  // function has TPU-incompatible ops, outside compilation can automatically
  // move the ops to execute on the CPU at the runtime.
  bool enable_soft_device_placement = 2;
}

message BatchOptions {
  // Number of scheduling threads for processing batches of work. Determines
  // the number of batches processed in parallel. This should be roughly in line
  // with the number of TPU cores available.
  int32 num_batch_threads = 1;

  // The maximum allowed batch size.
  int32 max_batch_size = 2;

  // Maximum number of microseconds to wait before outputting an incomplete
  // batch.
  int32 batch_timeout_micros = 3;

  // Optional list of allowed batch sizes. If left empty,
  // does nothing. Otherwise, supplies a list of batch sizes, causing the op
  // to pad batches up to one of those sizes. The entries must increase
  // monotonically, and the final entry must equal max_batch_size.
  repeated int32 allowed_batch_sizes = 4;

  // Maximum number of batches enqueued for processing before requests are
  // failed fast.
  int32 max_enqueued_batches = 5;

  // If set, disables large batch splitting which is an efficiency improvement
  // on batching to reduce padding inefficiency.
  bool disable_large_batch_splitting = 6;

  // Experimental features of batching. Everything inside is subject to change.
  message Experimental {
    // The component to be batched.
    // 1. Unset if it's for all TPU subgraphs.
    // 2. Set function_alias or concrete_function_name if it's for a function.
    // 3. Set signature_name if it's for a signature.
    oneof batch_component {
      // The function alias associated with the function. Function alias is
      // created during model export using the tf.saved_model.SaveOptions, and is
      // the recommended way to specify functions.
      string function_alias = 1;

      // The concreate name of the function. This is the name of the function as
      // it found in the GraphDef and the FunctionDefLibrary. This is NOT the
      // recommended way to specify functions, because concrete function names
      // change every time a model is exported.
      string concrete_function_name = 2;

      // The name of the signature.
      string signature_name = 3;
    }
  }

  Experimental experimental = 7;
}

message BFloat16OptimizationOptions {
  // Indicates where the BFloat16 optimization should be applied.
  enum Scope {
    // The scope currently defaults to TPU.
    DEFAULT = 0;
    // Apply the bfloat16 optimization to TPU computation.
    TPU = 1;
    // Apply the bfloat16 optimization to the entire model including CPU
    // computations.
    ALL = 2;
  }

  // This field indicates where the bfloat16 optimization should be applied.
  //
  // The scope defaults to TPU.
  Scope scope = 1;

  // If set, the normal safety checks are skipped. For example, if the model
  // already contains bfloat16 ops, the bfloat16 optimization will error because
  // pre-existing bfloat16 ops can cause issues with the optimization. By
  // setting this flag, the bfloat16 optimization will skip the check.
  //
  // This is an advanced feature and not recommended for almost all models.
  //
  // This flag is off by default.
  bool skip_safety_checks = 2;

  // Ops that should not be converted to bfloat16.
  // Inputs into these ops will be cast to float32, and outputs from these ops
  // will be cast back to bfloat16.
  repeated string filterlist = 3;
}

message XlaShardingOptions {
  // num_cores_per_replica for TPUReplicateMetadata.
  //
  // This is the number of cores you wish to split your model into using XLA
  // SPMD.
  int32 num_cores_per_replica = 1;

  // (optional) device_assignment for TPUReplicateMetadata.
  //
  // This is in a flattened [x, y, z, core] format (for
  // example, core 1 of the chip
  // located in 2,3,0 will be stored as [2,3,0,1]).
  //
  // If this is not specified, then the device assignments will utilize the same
  // topology as specified in the topology attribute.
  repeated int32 device_assignment = 2;

  // A serialized string of tensorflow.tpu.TopologyProto objects, used for
  // the topology attribute in TPUReplicateMetadata.
  //
  // You must specify the mesh_shape and device_coordinates attributes in
  // the topology object.
  //
  // This option is required for num_cores_per_replica > 1 cases due to
  // ambiguity of num_cores_per_replica, for example,
  // pf_1x2x1 with megacore and df_1x1
  // both have num_cores_per_replica = 2, but topology is (1,2,1,1) for pf and
  // (1,1,1,2) for df.
  // - For pf_1x2x1, mesh shape and device_coordinates looks like:
  //   mesh_shape = [1,2,1,1]
  //   device_coordinates=flatten([0,0,0,0], [0,1,0,0])
  // - For df_1x1, mesh shape and device_coordinates looks like:
  //   mesh_shape = [1,1,1,2]
  //   device_coordinates=flatten([0,0,0,0], [0,0,0,1])
  // - For df_2x2, mesh shape and device_coordinates looks like:
  //   mesh_shape = [2,2,1,2]
  //   device_coordinates=flatten(
  //    [0,0,0,0],[0,0,0,1],[0,1,0,0],[0,1,0,1]
  //    [1,0,0,0],[1,0,0,1],[1,1,0,0],[1,1,0,1])
  bytes topology = 3;
}

Approfondimento sul batch

Il batch viene utilizzato per migliorare la velocità effettiva e l'utilizzo della TPU. Consente di elaborare più richieste contemporaneamente. Durante l'addestramento, il raggruppamento può essere eseguito utilizzando tf.data. Durante l'inferenza, in genere viene eseguita aggiungendo un'operazione al grafico che raggruppa le richieste in entrata. L'operazione attende fino a quando non ha un numero sufficiente di richieste o viene raggiunto un timeout prima di generare un batch di grandi dimensioni dalle singole richieste. Per ulteriori informazioni sulle diverse opzioni di raggruppamento, tra cui dimensioni e timeout dei batch, consulta la sezione Definizione delle opzioni di batch.

raggruppamento nel grafico

Per impostazione predefinita, il convertitore inserisce l'operazione di raggruppamento direttamente prima del calcolo della TPU. Aggrega le funzioni TPU fornite dall'utente e qualsiasi calcolo TPU preesistente nel modello con operazioni di batching. È possibile eseguire l'override di questo comportamento predefinito indicando al convertitore quali funzioni e/o firme devono essere raggruppate.

L'esempio seguente mostra come aggiungere il raggruppamento predefinito.

batch_options {
  num_batch_threads: 2
  max_batch_size: 8
  batch_timeout_micros: 5000
  allowed_batch_sizes: 2
  allowed_batch_sizes: 4
  allowed_batch_sizes: 8
  max_enqueued_batches: 10
}

Raggruppamento di firme

Il raggruppamento in batch della firma raggruppa l'intero modello a partire dagli input della firma e andando agli output della firma. A differenza del comportamento di batch predefinito del convertitore, la firma batching batch sia il calcolo TPU sia il calcolo della CPU. Questo offre un aumento delle prestazioni dal 10% al 20% durante l'inferenza su alcuni modelli.

Come per tutti i processi di raggruppamento, anche in modalità Signature prevede requisiti di forma rigorosi. Per garantire che questi requisiti di forma siano soddisfatti, gli input delle firme devono avere forme che hanno almeno due dimensioni. La prima dimensione è la dimensione batch e deve avere -1. Ad esempio, (-1, 4), (-1) o (-1, 128, 4, 10) sono tutte forme di input valide. Se non è possibile, valuta l'utilizzo del comportamento di raggruppamento predefinito o della funzionalità di raggruppamento in batch.

Per utilizzare il raggruppamento delle firme, fornisci i nomi delle firme come signature_name utilizzando BatchOptions.

batch_options {
  num_batch_threads: 2
  max_batch_size: 8
  batch_timeout_micros: 5000
  allowed_batch_sizes: 2
  allowed_batch_sizes: 4
  allowed_batch_sizes: 8
  max_enqueued_batches: 10
  experimental {
    signature_name: "serving_default"
  }
}

Raggruppamento delle funzioni

Il raggruppamento delle funzioni può essere utilizzato per indicare al convertitore le funzioni da raggruppare. Per impostazione predefinita, il convertitore raggruppa tutte le funzioni TPU. Il batch di funzioni sostituisce questo comportamento predefinito.

Il batch di funzioni può essere utilizzato per il calcolo batch della CPU. Molti modelli registrano un miglioramento delle prestazioni quando il calcolo della CPU è in batch. Il modo migliore per eseguire il calcolo in batch della CPU è utilizzare il raggruppamento delle firme, ma potrebbe non funzionare con alcuni modelli. In questi casi, è possibile utilizzare il raggruppamento delle funzioni per eseguire il batch di una parte del calcolo della CPU oltre al calcolo della TPU. Tieni presente che l'operazione di raggruppamento non può essere eseguita sulla TPU, pertanto qualsiasi funzione di raggruppamento fornita deve essere chiamata sulla CPU.

Il raggruppamento delle funzioni può essere utilizzato anche per soddisfare i requisiti di forma rigorosi imposti dall'operazione di batch. Nei casi in cui le funzioni TPU non soddisfino i requisiti di forma dell'operazione di batch, è possibile utilizzare il raggruppamento in batch per indicare all'autore della conversione di raggruppare diverse funzioni.

Per utilizzarlo, genera un function_alias per la funzione da raggruppare. Puoi farlo trovando o creando nel modello una funzione che aggrega tutto ciò che vuoi in batch. Assicurati che questa funzione soddisfi i requisiti di forma rigorosi imposti dall'operazione di raggruppamento. Aggiungi @tf.function se non ne ha già uno. È importante fornire input_signature a @tf.function. La 0a dimensione deve essere None perché è la dimensione batch, quindi non può essere una dimensione fissa. Ad esempio, [None, 4], [None] o [None, 128, 4, 10] sono tutte forme di input valide. Quando salvi il modello, fornisci SaveOptions come quelli mostrati di seguito per assegnare a model.batch_func l'alias "batch_func". Dopodiché puoi passare l'alias di questa funzione all'utente che ha completato una conversione.

class ToyModel(tf.keras.Model):
  @tf.function(input_signature=[tf.TensorSpec(shape=[None, 10],
                                              dtype=tf.float32)])
  def batch_func(self, x):
    return x * 1.0

  ...

model = ToyModel()
save_options = tf.saved_model.SaveOptions(function_aliases={
    'batch_func': model.batch_func,
})
tf.saved_model.save(model, model_dir, options=save_options)

Quindi, passa i function_alias utilizzando il comando BatchOptions.

batch_options {
  num_batch_threads: 2
  max_batch_size: 8
  batch_timeout_micros: 5000
  allowed_batch_sizes: 2
  allowed_batch_sizes: 4
  allowed_batch_sizes: 8
  max_enqueued_batches: 10
  experimental {
    function_alias: "batch_func"
  }
}

Definizione delle opzioni di raggruppamento

  • num_batch_threads: (numero intero) numero di thread di pianificazione per l'elaborazione di batch di lavoro. Determina il numero di batch elaborati in parallelo. Dovrebbe essere all'incirca in linea con il numero di core TPU disponibili.
  • max_batch_size: dimensione (numero intero) massima consentita per il batch. Può essere superiore a allowed_batch_sizes per utilizzare la suddivisione in batch di grandi dimensioni.
  • batch_timeout_micros: (numero intero) numero massimo di microsecondi da attendere prima di inviare un batch incompleto.
  • allowed_batch_sizes: (elenco di numeri interi) se l'elenco non è vuoto, i gruppi verranno aggiunti alla dimensione più vicina nell'elenco. L'elenco deve essere monotonicamente in aumento e l'elemento finale deve essere inferiore o uguale a max_batch_size.
  • max_enqueued_batches: (numero intero) numero massimo di batch accodati per l'elaborazione prima che le richieste non vadano a buon fine.

Aggiornamento delle opzioni di raggruppamento esistenti

Puoi aggiungere o aggiornare le opzioni di raggruppamento eseguendo l'immagine Docker specificando batch_options e impostando disable_default_optimizations su true utilizzando il flag --converter_options_string. Le opzioni batch verranno applicate a ogni funzione TPU o a ogni operazione di batch preesistente.

batch_options {
  num_batch_threads: 2
  max_batch_size: 8
  batch_timeout_micros: 5000
  allowed_batch_sizes: 2
  allowed_batch_sizes: 4
  allowed_batch_sizes: 8
  max_enqueued_batches: 10
}
disable_default_optimizations=True

Requisiti per le forme di raggruppamento

I batch vengono creati concatenando i tensori di input tra le richieste lungo la loro dimensione batch (0a). I tensori di output sono suddivisi in base alla loro 0a dimensione. Per eseguire queste operazioni, l'operazione di raggruppamento ha requisiti di forma rigorosi per gli input e gli output.

Procedura dettagliata

Per comprendere questi requisiti, è utile comprendere prima come viene eseguito il batch. Nell'esempio che segue, raggruppa una semplice operazione tf.matmul.

def my_func(A, B)
    return tf.matmul(A, B)

La prima richiesta di inferenza produce gli input A e B con le forme (1, 3, 2) e (1, 2, 4) rispettivamente. La seconda richiesta di inferenza produce gli input A e B con le forme (2, 3, 2) e (2, 2, 4).

richiesta di inferenza 1

È stato raggiunto il timeout del raggruppamento. Il modello supporta una dimensione batch pari a 3, in modo che le richieste di inferenza n. 1 e n. 2 vengano raggruppate insieme senza alcuna spaziatura interna. I tensori in batch vengono formati concatenando le richieste n. 1 e n. 2 lungo la dimensione batch (0a). Poiché la A di 1 ha la forma (1, 3, 2) e la A di 2 ha la forma di (2, 3, 2), quando sono concatenate lungo la dimensione batch (0a), la forma risultante è (3, 3, 2).

richiesta in batch

Viene eseguito tf.matmul e produce un output con la forma (3, 3, 4).

richiesta matmul in batch

L'output di tf.matmul è in batch, quindi deve essere suddiviso nuovamente in richieste separate. L'operazione di raggruppamento lo fa suddividendo la dimensione batch (0a) di ogni tensore di output. Decide come suddividere la 0a dimensione in base alla forma degli input originali. Poiché le forme della richiesta n. 1 hanno una dimensione 0 pari a 1, il relativo output ha una dimensione 0 pari a 1 per una forma di (1, 3, 4). Poiché le forme della richiesta n. 2 hanno una dimensione 0 di 2, il relativo output ha una 0a dimensione di 2 per una forma di (2, 3, 4).

risultati della richiesta di inferenza

Requisiti per le forme

Per eseguire la concatenazione degli input e la suddivisione dell'output descritta in precedenza, l'operazione di raggruppamento ha i seguenti requisiti di forma:

  1. Gli input per il raggruppamento non possono essere scalari. Per concatenare la dimensione 0, i tensori devono avere almeno due dimensioni.

    Nella procedura dettagliata riportata sopra. Né A né B sono valori scalari.

    Il mancato rispetto di questo requisito causerà un errore come: Batching input tensors must have at least one dimension. Una semplice soluzione per questo errore è rendere lo scalare un vettore.

  2. In diverse richieste di inferenza (ad esempio chiamate di esecuzione di sessioni diverse), i tensori di input con lo stesso nome hanno la stessa dimensione per ogni dimensione, tranne la 0. Ciò consente di concatenare gli input in modo pulito nella 0a dimensione.

    Nella procedura dettagliata riportata sopra, la A della richiesta n. 1 ha la forma di (1, 3, 2). Ciò significa che qualsiasi richiesta futura deve produrre una forma con il modello (X, 3, 2). La richiesta n. 2 soddisfa questo requisito con (2, 3, 2). Allo stesso modo, la B della richiesta n. 1 ha una forma (1, 2, 4), quindi tutte le richieste future dovranno generare una forma con il pattern (X, 2, 4).

    Il mancato rispetto di questo requisito causerà un errore come: Dimensions of inputs should match.

  3. Per una determinata richiesta di inferenza, tutti gli input devono avere la stessa dimensione 0. Se diversi tensori di input dell'operazione di raggruppamento hanno dimensioni 0 diverse, l'operazione di raggruppamento non sa come suddividere i tensori di output.

    Nella procedura dettagliata precedente, i tensori della richiesta n. 1 hanno tutti una dimensione 0 pari a 1. Ciò consente all'operazione di raggruppamento di sapere che il suo output deve avere una dimensione 0 pari a 1. Allo stesso modo, i tensori della richiesta n. 2 hanno una dimensione di 0a pari a 2, quindi il suo output avrà una dimensione di 0a pari a 2. Quando l'operazione di batch suddivide la forma finale di (3, 3, 4), produce (1, 3, 4) per la richiesta n. 1 e (2, 3, 4) per la richiesta n. 2.

    Il mancato rispetto di questo requisito comporterà errori come: Batching input tensors supplied in a given op invocation must have equal 0th-dimension size.

  4. La dimensione 0a della forma di ogni tensore di output deve essere la somma della prima dimensione di tutti i tensori di input (più qualsiasi spaziatura interna introdotta dall'operazione di raggruppamento per soddisfare il successivo allowed_batch_size più grande). In questo modo l'operazione di raggruppamento può suddividere i tensori di output lungo la 0a dimensione in base alla 0a dimensione dei tensori di input.

    Nella procedura dettagliata precedente, i tensori di input hanno una dimensione 0 pari a 1 dalla richiesta 1 e 2 dalla richiesta 2. Pertanto, ogni tensore di output deve avere una dimensione 0 pari a 3 perché 1 + 2=3. Il tensore di output (3, 3, 4) soddisfa questo requisito. Se 3 non fosse una dimensione batch valida ma 4 lo fosse, l'operazione di batch avrebbe dovuto occupare la 0a dimensione degli input da 3 a 4. In questo caso, ogni tensore di output dovrebbe avere una dimensione 0 pari a 4.

    Il mancato rispetto di questo requisito comporterà un errore come: Batched output tensor's 0th dimension does not equal the sum of the 0th dimension sizes of the input tensors.

Risoluzione degli errori dei requisiti delle forme

Per soddisfare questi requisiti, valuta la possibilità di fornire una funzione o una firma diversa per il batch. Potrebbe anche essere necessario modificare le funzioni esistenti per soddisfare questi requisiti.

Se una funzione viene creata in batch, assicurati che le forme input_signature di @tf.function abbiano tutte None nella 0a dimensione (nota anche come dimensione batch). Se è in corso il raggruppamento di una firma, assicurati che tutti i suoi input abbiano -1 nella 0a dimensione.

L'operazione BatchFunction non supporta SparseTensors come input o output. Internamente, ogni tensore sparso è rappresentato come tre tensori separati che possono avere dimensioni di 0a dimensione diverse.