Partecipa al simposio Women in ML il 7 dicembre Registrati ora

Utilizzo del formato SavedModel

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza l'origine su GitHub Scarica quaderno

Un SavedModel contiene un programma TensorFlow completo, inclusi parametri addestrati (ad esempio, tf.Variable s) e calcolo. Non richiede l'esecuzione del codice di costruzione del modello originale, il che lo rende utile per la condivisione o la distribuzione con TFLite , TensorFlow.js , TensorFlow Serving o TensorFlow Hub .

Puoi salvare e caricare un modello nel formato SavedModel utilizzando le seguenti API:

Creazione di un modello salvato da Keras

Per una rapida introduzione, questa sezione esporta un modello Keras pre-addestrato e serve con esso le richieste di classificazione delle immagini. Il resto della guida inserirà i dettagli e discuterà altri modi per creare modelli salvati.

import os
import tempfile

from matplotlib import pyplot as plt
import numpy as np
import tensorflow as tf

tmpdir = tempfile.mkdtemp()
physical_devices = tf.config.list_physical_devices('GPU')
for device in physical_devices:
  tf.config.experimental.set_memory_growth(device, True)
file = tf.keras.utils.get_file(
    "grace_hopper.jpg",
    "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg")
img = tf.keras.utils.load_img(file, target_size=[224, 224])
plt.imshow(img)
plt.axis('off')
x = tf.keras.utils.img_to_array(img)
x = tf.keras.applications.mobilenet.preprocess_input(
    x[tf.newaxis,...])
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step
73728/61306 [====================================] - 0s 0us/step

png

Utilizzerai un'immagine di Grace Hopper come esempio in esecuzione e un modello di classificazione delle immagini pre-addestrato Keras poiché è facile da usare. Anche i modelli personalizzati funzionano e verranno trattati in dettaglio in seguito.

labels_path = tf.keras.utils.get_file(
    'ImageNetLabels.txt',
    'https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step
24576/10484 [======================================================================] - 0s 0us/step
pretrained_model = tf.keras.applications.MobileNet()
result_before_save = pretrained_model(x)

decoded = imagenet_labels[np.argsort(result_before_save)[0,::-1][:5]+1]

print("Result before saving:\n", decoded)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 0s 0us/step
17235968/17225924 [==============================] - 0s 0us/step
Result before saving:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

La previsione migliore per questa immagine è "uniforme militare".

mobilenet_save_path = os.path.join(tmpdir, "mobilenet/1/")
tf.saved_model.save(pretrained_model, mobilenet_save_path)
2021-10-27 01:24:27.831628: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/mobilenet/1/assets

Il percorso di salvataggio segue una convenzione utilizzata da TensorFlow Serving in cui l'ultimo componente del percorso ( 1/ qui) è un numero di versione per il modello: consente a strumenti come Tensorflow Serving di ragionare sulla relativa freschezza.

Puoi caricare di nuovo SavedModel in Python con tf.saved_model.load e vedere come è classificata l'immagine dell'ammiraglio Hopper.

loaded = tf.saved_model.load(mobilenet_save_path)
print(list(loaded.signatures.keys()))  # ["serving_default"]
['serving_default']

Le firme importate restituiscono sempre dizionari. Per personalizzare i nomi delle firme e le chiavi del dizionario di output, vedere Specifica delle firme durante l'esportazione .

infer = loaded.signatures["serving_default"]
print(infer.structured_outputs)
{'predictions': TensorSpec(shape=(None, 1000), dtype=tf.float32, name='predictions')}

L'esecuzione dell'inferenza da SavedModel fornisce lo stesso risultato del modello originale.

labeling = infer(tf.constant(x))[pretrained_model.output_names[0]]

decoded = imagenet_labels[np.argsort(labeling)[0,::-1][:5]+1]

print("Result after saving and loading:\n", decoded)
Result after saving and loading:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

Esecuzione di un modello salvato in TensorFlow Serving

I SavedModel sono utilizzabili da Python (ne parleremo più avanti), ma gli ambienti di produzione in genere utilizzano un servizio dedicato per l'inferenza senza eseguire codice Python. È facile da configurare da un modello salvato utilizzando TensorFlow Serving.

Consulta l' esercitazione REST di TensorFlow Serving per un esempio end-to-end di tensorflow.

Il formato SavedModel su disco

Un SavedModel è una directory contenente firme serializzate e lo stato necessario per eseguirle, inclusi valori di variabili e vocabolari.

ls {mobilenet_save_path}
assets  saved_model.pb  variables

Il file saved_model.pb memorizza il programma TensorFlow, o modello, e una serie di firme con nome, ciascuna delle quali identifica una funzione che accetta input tensoriali e produce output tensoriali.

SavedModels può contenere più varianti del modello (più v1.MetaGraphDefs , identificate con il flag --tag_set su saved_model_cli ), ma questo è raro. Le API che creano più varianti di un modello includono tf.Estimator.experimental_export_all_saved_models e in TensorFlow 1.x tf.saved_model.Builder .

saved_model_cli show --dir {mobilenet_save_path} --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "serving_default"

La directory delle variables contiene un punto di controllo dell'addestramento standard (consultare la guida ai punti di controllo dell'addestramento ).

ls {mobilenet_save_path}/variables
variables.data-00000-of-00001  variables.index

La directory degli assets contiene i file utilizzati dal grafico TensorFlow, ad esempio i file di testo utilizzati per inizializzare le tabelle del vocabolario. Non è utilizzato in questo esempio.

SavedModels può avere una directory assets.extra per tutti i file non utilizzati dal grafico TensorFlow, ad esempio informazioni per i consumatori su cosa fare con SavedModel. TensorFlow stesso non utilizza questa directory.

Salvataggio di un modello personalizzato

tf.saved_model.save supporta il salvataggio di oggetti tf.Module e le sue sottoclassi, come tf.keras.Layer e tf.keras.Model .

Diamo un'occhiata a un esempio di salvataggio e ripristino di un tf.Module .

class CustomModule(tf.Module):

  def __init__(self):
    super(CustomModule, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function
  def __call__(self, x):
    print('Tracing with', x)
    return x * self.v

  @tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
  def mutate(self, new_v):
    self.v.assign(new_v)

module = CustomModule()

Quando si salva un tf.Module , tutti gli attributi tf.Variable , i metodi tf.function tf.function e i tf.Module trovati tramite traversal ricorsivo vengono salvati. (Vedi il tutorial Checkpoint per ulteriori informazioni su questo attraversamento ricorsivo.) Tuttavia, tutti gli attributi, le funzioni e i dati Python vengono persi. Ciò significa che quando viene salvata una tf.function , non viene salvato alcun codice Python.

Se non viene salvato alcun codice Python, in che modo SavedModel sa come ripristinare la funzione?

In breve, tf.function funziona tracciando il codice Python per generare una ConcreteFunction (un wrapper richiamabile attorno a tf.Graph ). Quando salvi un tf.function , stai davvero salvando la cache di ConcreteFunctions di tf.function .

Per saperne di più sulla relazione tra tf.function e ConcreteFunctions, vedere la guida tf.function .

module_no_signatures_path = os.path.join(tmpdir, 'module_no_signatures')
module(tf.constant(0.))
print('Saving model...')
tf.saved_model.save(module, module_no_signatures_path)
Tracing with Tensor("x:0", shape=(), dtype=float32)
Saving model...
Tracing with Tensor("x:0", shape=(), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/module_no_signatures/assets

Caricamento e utilizzo di un modello personalizzato

Quando carichi un SavedModel in Python, tutti gli attributi tf.Variable , i metodi tf.function tf.function e tf.Module vengono ripristinati nella stessa struttura di oggetti del tf.Module salvato originale.

imported = tf.saved_model.load(module_no_signatures_path)
assert imported(tf.constant(3.)).numpy() == 3
imported.mutate(tf.constant(2.))
assert imported(tf.constant(3.)).numpy() == 6

Poiché non viene salvato alcun codice Python, la chiamata a tf.function con una nuova firma di input avrà esito negativo:

imported(tf.constant([3.]))
ValueError: Could not find matching function to call for canonicalized inputs ((,), {}). Only existing signatures are [((TensorSpec(shape=(), dtype=tf.float32, name=u'x'),), {})].

Messa a punto di base

Sono disponibili oggetti variabili ed è possibile eseguire il backprop tramite le funzioni importate. Questo è sufficiente per mettere a punto (cioè riqualificare) un SavedModel in casi semplici.

optimizer = tf.optimizers.SGD(0.05)

def train_step():
  with tf.GradientTape() as tape:
    loss = (10. - imported(tf.constant(2.))) ** 2
  variables = tape.watched_variables()
  grads = tape.gradient(loss, variables)
  optimizer.apply_gradients(zip(grads, variables))
  return loss
for _ in range(10):
  # "v" approaches 5, "loss" approaches 0
  print("loss={:.2f} v={:.2f}".format(train_step(), imported.v.numpy()))
loss=36.00 v=3.20
loss=12.96 v=3.92
loss=4.67 v=4.35
loss=1.68 v=4.61
loss=0.60 v=4.77
loss=0.22 v=4.86
loss=0.08 v=4.92
loss=0.03 v=4.95
loss=0.01 v=4.97
loss=0.00 v=4.98

Messa a punto generale

Un SavedModel di Keras fornisce più dettagli di un semplice __call__ per affrontare casi più avanzati di messa a punto. TensorFlow Hub consiglia di fornire quanto segue, se applicabile, in SavedModels condivisi ai fini della messa a punto:

  • Se il modello usa dropout o un'altra tecnica in cui il passaggio in avanti differisce tra training e inferenza (come la normalizzazione batch), il metodo __call__ accetta un argomento training= opzionale con valore Python che per impostazione predefinita è False ma può essere impostato su True .
  • Accanto all'attributo __call__ , ci sono gli attributi .variable e .trainable_variable con i corrispondenti elenchi di variabili. Una variabile originariamente addestrabile ma destinata a essere congelata durante l'ottimizzazione viene omessa da .trainable_variables .
  • Per il bene di framework come Keras che rappresentano i regolatori di peso come attributi di livelli o sottomodelli, può esserci anche un attributo .regularization_losses . Contiene un elenco di funzioni ad argomento zero i cui valori sono destinati ad essere aggiunti alla perdita totale.

Tornando all'esempio iniziale di MobileNet, puoi vederne alcuni in azione:

loaded = tf.saved_model.load(mobilenet_save_path)
print("MobileNet has {} trainable variables: {}, ...".format(
          len(loaded.trainable_variables),
          ", ".join([v.name for v in loaded.trainable_variables[:5]])))
MobileNet has 83 trainable variables: conv1/kernel:0, conv1_bn/gamma:0, conv1_bn/beta:0, conv_dw_1/depthwise_kernel:0, conv_dw_1_bn/gamma:0, ...
trainable_variable_ids = {id(v) for v in loaded.trainable_variables}
non_trainable_variables = [v for v in loaded.variables
                           if id(v) not in trainable_variable_ids]
print("MobileNet also has {} non-trainable variables: {}, ...".format(
          len(non_trainable_variables),
          ", ".join([v.name for v in non_trainable_variables[:3]])))
MobileNet also has 54 non-trainable variables: conv1_bn/moving_mean:0, conv1_bn/moving_variance:0, conv_dw_1_bn/moving_mean:0, ...

Specifica delle firme durante l'esportazione

Strumenti come TensorFlow Serving e saved_model_cli possono interagire con SavedModels. Per aiutare questi strumenti a determinare quali ConcreteFunctions utilizzare, è necessario specificare le firme di servizio. tf.keras.Model s specifica automaticamente le firme di servizio, ma dovrai dichiarare esplicitamente una firma di servizio per i nostri moduli personalizzati.

Per impostazione predefinita, nessuna firma viene dichiarata in un tf.Module personalizzato.

assert len(imported.signatures) == 0

Per dichiarare una firma di servizio, specifica una ConcreteFunction usando le signatures kwarg. Quando si specifica una singola firma, la sua chiave di firma sarà 'serving_default' , che viene salvata come costante tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY .

module_with_signature_path = os.path.join(tmpdir, 'module_with_signature')
call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
tf.saved_model.save(module, module_with_signature_path, signatures=call)
Tracing with Tensor("x:0", dtype=float32)
Tracing with Tensor("x:0", dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/module_with_signature/assets
imported_with_signatures = tf.saved_model.load(module_with_signature_path)
list(imported_with_signatures.signatures.keys())
['serving_default']

Per esportare più firme, passa un dizionario di chiavi di firma a ConcreteFunctions. Ogni chiave di firma corrisponde a una ConcreteFunction.

module_multiple_signatures_path = os.path.join(tmpdir, 'module_with_multiple_signatures')
signatures = {"serving_default": call,
              "array_input": module.__call__.get_concrete_function(tf.TensorSpec([None], tf.float32))}

tf.saved_model.save(module, module_multiple_signatures_path, signatures=signatures)
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/module_with_multiple_signatures/assets
imported_with_multiple_signatures = tf.saved_model.load(module_multiple_signatures_path)
list(imported_with_multiple_signatures.signatures.keys())
['serving_default', 'array_input']

Per impostazione predefinita, i nomi dei tensori di output sono abbastanza generici, come output_0 . Per controllare i nomi degli output, modifica la tua tf.function per restituire un dizionario che associa i nomi degli output agli output. I nomi degli input sono derivati ​​dai nomi arg della funzione Python.

class CustomModuleWithOutputName(tf.Module):
  def __init__(self):
    super(CustomModuleWithOutputName, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
  def __call__(self, x):
    return {'custom_output_name': x * self.v}

module_output = CustomModuleWithOutputName()
call_output = module_output.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
module_output_path = os.path.join(tmpdir, 'module_with_output_name')
tf.saved_model.save(module_output, module_output_path,
                    signatures={'serving_default': call_output})
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/module_with_output_name/assets
imported_with_output_name = tf.saved_model.load(module_output_path)
imported_with_output_name.signatures['serving_default'].structured_outputs
{'custom_output_name': TensorSpec(shape=(), dtype=tf.float32, name='custom_output_name')}

Carica un modello salvato in C++

La versione C++ del caricatore SavedModel fornisce un'API per caricare un SavedModel da un percorso, consentendo SessionOptions e RunOptions. Devi specificare i tag associati al grafico da caricare. La versione caricata di SavedModel è denominata SavedModelBundle e contiene MetaGraphDef e la sessione in cui viene caricata.

const string export_dir = ...
SavedModelBundle bundle;
...
LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain},
               &bundle);

Dettagli dell'interfaccia della riga di comando di SavedModel

È possibile utilizzare la SavedModel Command Line Interface (CLI) per ispezionare ed eseguire un SavedModel. Ad esempio, è possibile utilizzare la CLI per ispezionare i SignatureDef del modello. L'interfaccia a riga di comando consente di confermare rapidamente che il tipo e la forma del tensore di input corrispondano al modello. Inoltre, se desideri testare il tuo modello, puoi utilizzare la CLI per eseguire un controllo di integrità passando input di esempio in vari formati (ad esempio, espressioni Python) e quindi recuperando l'output.

Installa la CLI SavedModel

In generale, puoi installare TensorFlow in uno dei due modi seguenti:

  • Installando un binario TensorFlow predefinito.
  • Costruendo TensorFlow dal codice sorgente.

Se hai installato TensorFlow tramite un binario TensorFlow predefinito, la CLI SavedModel è già installata sul tuo sistema in percorso bin/saved_model_cli .

Se hai creato TensorFlow dal codice sorgente, devi eseguire il seguente comando aggiuntivo per creare saved_model_cli :

$ bazel build tensorflow/python/tools:saved_model_cli

Panoramica dei comandi

La CLI SavedModel supporta i seguenti due comandi su un SavedModel:

  • show , che mostra i calcoli disponibili da un SavedModel.
  • run , che esegue un calcolo da un SavedModel.

show comando

Un SavedModel contiene una o più varianti del modello (tecnicamente, v1.MetaGraphDef s), identificate dai relativi set di tag. Per servire un modello, potresti chiederti che tipo di SignatureDef ci sono in ciascuna variante del modello e quali sono i loro input e output. Il comando show consente di esaminare i contenuti di SavedModel in ordine gerarchico. Ecco la sintassi:

usage: saved_model_cli show [-h] --dir DIR [--all]
[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]

Ad esempio, il comando seguente mostra tutti i set di tag disponibili in SavedModel:

$ saved_model_cli show --dir /tmp/saved_model_dir
The given SavedModel contains the following tag-sets:
serve
serve, gpu

Il comando seguente mostra tutte le chiavi SignatureDef disponibili per un set di tag:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve
The given SavedModel `MetaGraphDef` contains `SignatureDefs` with the
following keys:
SignatureDef key: "classify_x2_to_y3"
SignatureDef key: "classify_x_to_y"
SignatureDef key: "regress_x2_to_y3"
SignatureDef key: "regress_x_to_y"
SignatureDef key: "regress_x_to_y2"
SignatureDef key: "serving_default"

Se sono presenti più tag nel set di tag, è necessario specificare tutti i tag, ciascuno separato da una virgola. Per esempio:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu

Per mostrare tutti gli input e gli output TensorInfo per uno specifico SignatureDef , passare la chiave SignatureDef all'opzione signature_def . Questo è molto utile quando vuoi conoscere il valore della chiave del tensore, dtype e la forma dei tensori di input per eseguire il grafico di calcolo in un secondo momento. Per esempio:

$ saved_model_cli show --dir \
/tmp/saved_model_dir --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['x'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: x:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['y'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: y:0
Method name is: tensorflow/serving/predict

Per mostrare tutte le informazioni disponibili in SavedModel, utilizzare l'opzione --all . Per esempio:

$ saved_model_cli show --dir /tmp/saved_model_dir --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classify_x2_to_y3']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x2:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y3:0
  Method name is: tensorflow/serving/classify

...

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['x'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['y'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y:0
  Method name is: tensorflow/serving/predict

run il comando

Richiamare il comando di run per eseguire un calcolo del grafico, passando gli input e quindi visualizzando (e facoltativamente salvando) gli output. Ecco la sintassi:

usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def
                           SIGNATURE_DEF_KEY [--inputs INPUTS]
                           [--input_exprs INPUT_EXPRS]
                           [--input_examples INPUT_EXAMPLES] [--outdir OUTDIR]
                           [--overwrite] [--tf_debug]

Il comando run fornisce i tre modi seguenti per passare gli input al modello:

  • L'opzione --inputs ti consente di passare numpy ndarray nei file.
  • L'opzione --input_exprs ti consente di passare le espressioni Python.
  • --input_examples opzione ti consente di passare tf.train.Example .

--inputs

Per passare i dati di input nei file, specifica l'opzione --inputs , che assume il seguente formato generale:

--inputs <INPUTS>

dove INPUTS è uno dei seguenti formati:

  • <input_key>=<filename>
  • <input_key>=<filename>[<variable_name>]

Puoi passare più INGRESSI . Se si passano più input, utilizzare un punto e virgola per separare ciascuno degli INPUTS .

saved_model_cli usa numpy.load per caricare il nome del file . Il nome del file può essere in uno dei seguenti formati:

  • .npy
  • .npz
  • formato sottaceto

Un file .npy contiene sempre un numpy ndarray. Pertanto, durante il caricamento da un file .npy , il contenuto verrà assegnato direttamente al tensore di input specificato. Se specifichi un nome_variabile con quel file .npy , il nome_variabile verrà ignorato e verrà emesso un avviso.

Quando si esegue il caricamento da un .npz (zip), è possibile specificare facoltativamente un nome_variabile per identificare la variabile all'interno del file zip da caricare per la chiave tensore di input. Se non specifichi un nome_variabile , la CLI SavedModel verificherà che un solo file sia incluso nel file zip e lo caricherà per la chiave tensore di input specificata.

Durante il caricamento da un file pickle, se tra parentesi quadre non viene specificato alcun variable_name , tutto ciò che si trova all'interno del file pickle verrà passato alla chiave tensore di input specificata. In caso contrario, la CLI SavedModel presumerà che un dizionario sia archiviato nel file pickle e verrà utilizzato il valore corrispondente a nome_variabile .

--input_exprs

Per passare gli input attraverso le espressioni Python, specifica l'opzione --input_exprs . Questo può essere utile quando non hai file di dati in giro, ma vuoi comunque controllare il modello con alcuni semplici input che corrispondono al dtype e alla forma di SignatureDef s del modello. Per esempio:

`<input_key>=[[1],[2],[3]]`

Oltre alle espressioni Python, puoi anche passare funzioni numpy. Per esempio:

`<input_key>=np.ones((32,32,3))`

(Nota che il modulo numpy è già disponibile per te come np .)

--input_examples

Per passare tf.train.Example come input, specifica l'opzione --input_examples . Per ogni chiave di input, richiede un elenco di dizionari, dove ogni dizionario è un'istanza di tf.train.Example . Le chiavi del dizionario sono le caratteristiche ei valori sono le liste di valori per ciascuna caratteristica. Per esempio:

`<input_key>=[{"age":[22,24],"education":["BS","MS"]}]`

Salva output

Per impostazione predefinita, la CLI SavedModel scrive l'output su stdout. Se una directory viene passata all'opzione --outdir , gli output verranno salvati come file .npy denominati in base alle chiavi del tensore di output nella directory specificata.

Usa --overwrite per sovrascrivere i file di output esistenti.