דף זה תורגם על ידי Cloud Translation API.
Switch to English

באמצעות אוסף AOT

מה זה tfcompile?

tfcompile הוא כלי עצמאי tfcompile בזמן (AOT) אוצר גרפים של TensorFlow לקוד הפעלה. זה יכול להפחית את הגודל הבינארי הכולל, וגם למנוע כמה תקורות זמן ריצה. מקרה שימוש טיפוסי של tfcompile הוא להרכיב גרף סיכום לקוד הפעלה למכשירים ניידים.

הגרף TensorFlow מבוצע בדרך כלל על ידי זמן הריצה של TensorFlow. זה כרוך בתקורה של זמן ריצה לצורך ביצוע כל צומת בתרשים. זה מוביל גם לגודל בינארי כולל יותר, מכיוון שהקוד לזמן הריצה של TensorFlow צריך להיות זמין, בנוסף לתרשים עצמו. קוד ההפעלה המיוצר על ידי tfcompile אינו משתמש בזמן הריצה של TensorFlow, ויש לו רק תלות בגרעינים המשמשים למעשה בחישוב.

המהדר בנוי על גבי מסגרת ה- XLA. הקוד המגשר בין TensorFlow למסגרת XLA שוכן תחת זרימת טסור / מהדר .

מה עושה tfcompile?

tfcompile לוקח תת-גרף, המזוהה על ידי מושגי TensorFlow של הזנות ושליפות, ומייצר פונקציה שמיישמת את אותו תת-גרף. feeds הם טיעוני הקלט לפונקציה, fetches הם ארגומנטים הפלט של הפונקציה. חייבים להיות מוגדרים במלואם על ידי הזנות; תת-הגרף הגזום שהתקבל לא יכול להכיל מצייני מיקום או צמתים משתנים. מקובל לציין את כל מצייני המיקום והמשתנים כעדכונים, מה שמבטיח כי תת-הגרף המתקבל כבר לא מכיל צמתים אלה. הפונקציה שנוצרה נארזת כ- cc_library , כאשר קובץ כותרת מייצא את חתימת הפונקציה וקובץ אובייקט המכיל את היישום. המשתמש כותב קוד כדי להפעיל את הפונקציה שנוצרה בהתאם.

באמצעות tfcompile

פרק זה מפרט שלבים ברמה גבוהה לייצור בינארי הניתן להפעלה עם tfcompile מתחתית TensorFlow. השלבים הם:

  • שלב 1: קבע את התצורה של המשנה לתצוגה
  • שלב 2: השתמש tf_library build כדי להרכיב את תת tf_library
  • שלב 3: כתוב קוד כדי להפעיל את הסאבגרף
  • שלב 4: צור את הבינארי הסופי

שלב 1: קבע את התצורה של המשנה לתצוגה

זהה את העדכונים והשליפות התואמים את טיעוני הקלט והפלט של הפונקציה שנוצרה. ואז להגדיר את feeds ואת fetches בתוך tensorflow.tf2xla.Config פרוטו.

 # 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" }
}
 

שלב 2: השתמש במאקרו tf_library build כדי להרכיב את תת המשנה

שלב זה ממיר את הגרף למצב cc_library באמצעות המקרו tf_library build. cc_library ה- cc_library מורכב מקובץ אובייקטים המכיל את הקוד שנוצר מהגרף, יחד עם קובץ כותרת שמעניק גישה לקוד שנוצר. tf_library משתמש tfcompile כדי להרכיב את הגרף TensorFlow לקוד הפעלה.

 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",
)
 

כדי ליצור את הפרוטו של GraphDef (test_graph_tfmatmul.pb) לדוגמה זו, הפעל make_test_graphs.py וציין את מיקום הפלט עם דגל --out_dir.

גרפים אופייניים מכילים Variables המייצגים את המשקולות הנלמדים באמצעות הדרכה, אך tfcompile לא יכול להרכיב תת גרף המכיל Variables . הכלי freeze_graph.py ממיר משתנים לקבועים, תוך שימוש בערכים המאוחסנים בקובץ מחסום. לנוחיות, המאקרו tf_library תומך בטיעון freeze_checkpoint , שמריץ את הכלי. לדוגמאות נוספות ראו זרימת tensor / מהדר / aot / tests / BUILD .

קבועים המופיעים בתת-המשנה הידור מורכבים ישירות לקוד שנוצר. כדי להעביר את הקבועים לפונקציה שנוצרה, במקום להכניס אותם, פשוט העבירו אותם כהזנות.

לפרטים אודות מאקרו ה- tf_library build ראה tfcompile.bzl .

לפרטים אודות הכלי tfcompile הבסיסי, ראה tfcompile_main.cc .

שלב 3: כתוב קוד כדי להפעיל את הסאבגרף

שלב זה משתמש בקובץ הכותרת ( test_graph_tfmatmul.h ) שנוצר על ידי המאקרו build tf_library בשלב הקודם כדי להפעיל את הקוד שנוצר. קובץ הכותרת ממוקם bazel-bin ה- bazel-bin המתאים לחבילת build, והוא נקרא על סמך תכונת השם שנקבעה tf_library build tf_library . לדוגמה, הכותרת שנוצרה עבור test_graph_tfmatmul תהיה test_graph_tfmatmul.h . להלן גרסה מקוצרת של מה שנוצר. הקובץ שנוצר, bazel-bin , מכיל הערות שימושיות נוספות.

 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
 

המחלקה C ++ MatMulComp נקראת MatMulComp במרחב השמות foo::bar , מכיוון שזה היה cpp_class שצוין במאקרו tf_library . לכל המחלקות שנוצרו יש ממשק API דומה, כאשר ההבדל היחיד הוא השיטות להתמודדות עם מאגרי ארגז ותוצאה. אלה שיטות להשתנות בהתאם למספר סוגים של מאגרים, אשר פורטו על ידי feed ו fetch טיעונים אל tf_library מאקרו.

ישנם שלושה סוגים של מאגרים המנוהלים במסגרת המחלקה שנוצרה: args המייצגים את התשומות, results המייצגות את התפוקות, temps המייצגים מאגרים זמניים המשמשים פנימית לביצוע החישוב. כברירת מחדל, כל מופע של המחלקה שנוצרה מקצה ומנהל עבורכם את כל המאגרים הללו. ניתן AllocMode בטיעון בנאי AllocMode כדי לשנות התנהגות זו. כל המאגרים מיושרים לגבולות 64 בתים.

מחלקת C ++ שנוצרה היא רק עטיפה סביב הקוד ברמה הנמוכה שנוצרה על ידי XLA.

דוגמה tfcompile_test.cc הפונקציה שנוצרה על בסיס 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;
}
 

שלב 4: צור את הבינארי הסופי

שלב זה משלב את הספריה שנוצרה על ידי tf_library בשלב 2 ואת הקוד שנכתב בשלב 3 ליצירת בינארי סופי. להלן דוגמה לקובץ 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",
    ]
)