Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Utilizzo della compilazione AOT

Cos'è tfcompile?

tfcompile è uno strumento autonomo che in anticipo (AOT) compila i grafici TensorFlow in codice eseguibile. Può ridurre la dimensione binaria totale e anche evitare alcuni sovraccarichi di runtime. Un tipico caso d'uso di tfcompile consiste nel compilare un grafico di inferenza in codice eseguibile per dispositivi mobili.

Il grafico TensorFlow viene normalmente eseguito dal runtime TensorFlow. Ciò comporta un certo sovraccarico di runtime per l'esecuzione di ogni nodo nel grafico. Ciò porta anche a una dimensione binaria totale maggiore, poiché il codice per il runtime di TensorFlow deve essere disponibile, oltre al grafico stesso. Il codice eseguibile prodotto da tfcompile non utilizza il runtime di TensorFlow e ha solo dipendenze dai kernel effettivamente utilizzati nel calcolo.

Il compilatore si basa sul framework XLA. Il codice che collega TensorFlow al framework XLA risiede in tensorflow / compiler .

Cosa fa tfcompile?

tfcompile prende un sottografo, identificato dai concetti TensorFlow di feed e fetch, e genera una funzione che implementa quel sottografo. I feeds sono gli argomenti di input per la funzione e i fetches sono gli argomenti di output per la funzione. Tutti gli input devono essere completamente specificati dai feed; il sottografo tagliato risultante non può contenere nodi Segnaposto o Variabile. È comune specificare tutti i segnaposto e le variabili come feed, il che garantisce che il sottografo risultante non contenga più questi nodi. La funzione generata è confezionata come cc_library , con un file di intestazione che esporta la firma della funzione e un file oggetto contenente l'implementazione. L'utente scrive il codice per richiamare la funzione generata in modo appropriato.

Utilizzando tfcompile

Questa sezione descrive in dettaglio i passaggi di alto livello per la generazione di un file binario eseguibile con tfcompile da un sottografo TensorFlow. I passaggi sono:

  • Passaggio 1: configurare il sottografo da compilare
  • Passaggio 2: utilizzare la macro di build tf_library per compilare il sottografo
  • Passaggio 3: scrivere il codice per richiamare il sottografo
  • Passaggio 4: creare il file binario finale

Passaggio 1: configurare il sottografo da compilare

Identifica i feed e i recuperi che corrispondono agli argomenti di input e output per la funzione generata. Poi configurare le feeds e fetches in un tensorflow.tf2xla.Config proto.

# Each feed is a positional input argument for the generated function.  The order
# of each entry matches the order of each input argument.  Here “x_hold” and “y_hold”
# refer to the names of placeholder nodes defined in the graph.
feed {
  id { node_name: "x_hold" }
  shape {
    dim { size: 2 }
    dim { size: 3 }
  }
}
feed {
  id { node_name: "y_hold" }
  shape {
    dim { size: 3 }
    dim { size: 2 }
  }
}

# Each fetch is a positional output argument for the generated function.  The order
# of each entry matches the order of each output argument.  Here “x_y_prod”
# refers to the name of a matmul node defined in the graph.
fetch {
  id { node_name: "x_y_prod" }
}

Passaggio 2: utilizzare la macro build tf_library per compilare il sottografo

Questo passaggio converte il grafico in una cc_library utilizzando la macro build tf_library . La cc_library composta da un file oggetto contenente il codice generato dal grafico, insieme a un file di intestazione che dà accesso al codice generato. tf_library utilizza tfcompile per compilare il grafico TensorFlow in codice eseguibile.

load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")

# Use the tf_library macro to compile your graph into executable code.
tf_library(
    # name is used to generate the following underlying build rules:
    # <name>           : cc_library packaging the generated header and object files
    # <name>_test      : cc_test containing a simple test and benchmark
    # <name>_benchmark : cc_binary containing a stand-alone benchmark with minimal deps;
    #                    can be run on a mobile device
    name = "test_graph_tfmatmul",
    # cpp_class specifies the name of the generated C++ class, with namespaces allowed.
    # The class will be generated in the given namespace(s), or if no namespaces are
    # given, within the global namespace.
    cpp_class = "foo::bar::MatMulComp",
    # graph is the input GraphDef proto, by default expected in binary format.  To
    # use the text format instead, just use the ‘.pbtxt’ suffix.  A subgraph will be
    # created from this input graph, with feeds as inputs and fetches as outputs.
    # No Placeholder or Variable ops may exist in this subgraph.
    graph = "test_graph_tfmatmul.pb",
    # config is the input Config proto, by default expected in binary format.  To
    # use the text format instead, use the ‘.pbtxt’ suffix.  This is where the
    # feeds and fetches were specified above, in the previous step.
    config = "test_graph_tfmatmul.config.pbtxt",
)

Per generare il protocollo GraphDef (test_graph_tfmatmul.pb) per questo esempio, eseguire make_test_graphs.py e specificare la posizione di output con il flag --out_dir.

I grafici tipici contengono Variables rappresentano i pesi appresi tramite l'addestramento, ma tfcompile non può compilare un sottografo che contenga Variables . Lo strumento freeze_graph.py converte le variabili in costanti, utilizzando i valori memorizzati in un file di checkpoint. Per comodità, la macro tf_library supporta l'argomento freeze_checkpoint , che esegue lo strumento. Per ulteriori esempi, vedere tensorflow / compiler / aot / tests / BUILD .

Le costanti che compaiono nel sottografo compilato vengono compilate direttamente nel codice generato. Per passare le costanti alla funzione generata, invece di averle compilate, passale semplicemente come feed.

Per i dettagli sulla macro build tf_library , vedere tfcompile.bzl .

Per i dettagli sullo strumento tfcompile sottostante, vedere tfcompile_main.cc .

Passaggio 3: scrivere il codice per richiamare il sottografo

Questo passaggio utilizza il file di intestazione ( test_graph_tfmatmul.h ) generato dalla macro build tf_library nel passaggio precedente per richiamare il codice generato. Il file di intestazione si trova nella directory bazel-bin corrispondente al pacchetto di build ed è denominato in base all'attributo name impostato nella macro di build tf_library . Ad esempio, l'intestazione generata per test_graph_tfmatmul sarebbe test_graph_tfmatmul.h . Di seguito è riportata una versione abbreviata di ciò che viene generato. Il file generato, in bazel-bin , contiene ulteriori commenti utili.

namespace foo {
namespace bar {

// MatMulComp represents a computation previously specified in a
// TensorFlow graph, now compiled into executable code.
class MatMulComp {
 public:
  // AllocMode controls the buffer allocation mode.
  enum class AllocMode {
    ARGS_RESULTS_AND_TEMPS,  // Allocate arg, result and temp buffers
    RESULTS_AND_TEMPS_ONLY,  // Only allocate result and temp buffers
  };

  MatMulComp(AllocMode mode = AllocMode::ARGS_RESULTS_AND_TEMPS);
  ~MatMulComp();

  // Runs the computation, with inputs read from arg buffers, and outputs
  // written to result buffers. Returns true on success and false on failure.
  bool Run();

  // Arg methods for managing input buffers. Buffers are in row-major order.
  // There is a set of methods for each positional argument.
  void** args();

  void set_arg0_data(float* data);
  float* arg0_data();
  float& arg0(size_t dim0, size_t dim1);

  void set_arg1_data(float* data);
  float* arg1_data();
  float& arg1(size_t dim0, size_t dim1);

  // Result methods for managing output buffers. Buffers are in row-major order.
  // Must only be called after a successful Run call. There is a set of methods
  // for each positional result.
  void** results();


  float* result0_data();
  float& result0(size_t dim0, size_t dim1);
};

}  // end namespace bar
}  // end namespace foo

La classe C ++ generata si chiama MatMulComp nello spazio dei nomi foo::bar , perché quella era la cpp_class specificata nella macro tf_library . Tutte le classi generate hanno un'API simile, con l'unica differenza che sono i metodi per gestire arg e buffer dei risultati. Questi metodi differiscono in base al numero e al tipo di buffer, specificati dagli argomenti feed e fetch nella macro tf_library .

Esistono tre tipi di buffer gestiti all'interno della classe generata: args rappresentano gli input, i results rappresentano gli output e temps rappresentano i buffer temporanei utilizzati internamente per eseguire il calcolo. Per impostazione predefinita, ogni istanza della classe generata alloca e gestisce tutti questi buffer per te. L'argomento del costruttore AllocMode può essere utilizzato per modificare questo comportamento. Tutti i buffer sono allineati ai limiti di 64 byte.

La classe C ++ generata è solo un wrapper del codice di basso livello generato da XLA.

Esempio di invocazione della funzione generata in base a tfcompile_test.cc :

#define EIGEN_USE_THREADS
#define EIGEN_USE_CUSTOM_THREAD_POOL

#include <iostream>
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/aot/tests/test_graph_tfmatmul.h" // generated

int main(int argc, char** argv) {
  Eigen::ThreadPool tp(2);  // Size the thread pool as appropriate.
  Eigen::ThreadPoolDevice device(&tp, tp.NumThreads());


  foo::bar::MatMulComp matmul;
  matmul.set_thread_pool(&device);

  // Set up args and run the computation.
  const float args[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
  std::copy(args + 0, args + 6, matmul.arg0_data());
  std::copy(args + 6, args + 12, matmul.arg1_data());
  matmul.Run();

  // Check result
  if (matmul.result0(0, 0) == 58) {
    std::cout << "Success" << std::endl;
  } else {
    std::cout << "Failed. Expected value 58 at 0,0. Got:"
              << matmul.result0(0, 0) << std::endl;
  }

  return 0;
}

Passaggio 4: creare il file binario finale

Questo passaggio combina la libreria generata da tf_library nel passaggio 2 e il codice scritto nel passaggio 3 per creare un binario finale. Di seguito è riportato un esempio di file BUILD bazel .

# Example of linking your binary
# Also see //tensorflow/compiler/aot/tests/BUILD
load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")

# The same tf_library call from step 2 above.
tf_library(
    name = "test_graph_tfmatmul",
    ...
)

# The executable code generated by tf_library can then be linked into your code.
cc_binary(
    name = "my_binary",
    srcs = [
        "my_code.cc",  # include test_graph_tfmatmul.h to access the generated header
    ],
    deps = [
        ":test_graph_tfmatmul",  # link in the generated object file
        "//third_party/eigen3",
    ],
    linkopts = [
          "-lpthread",
    ]
)