ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

باستخدام تجميع AOT

ما هو tfcompile؟

tfcompile هي أداة قائمة بذاتها (AOT) تجمع الرسوم البيانية TensorFlow في كود قابل للتنفيذ. يمكن أن يقلل الحجم الكلي الثنائي ، وكذلك تجنب بعض النفقات العامة لوقت التشغيل. حالة الاستخدام النموذجية لـ tfcompile هي تجميع رسم بياني للاستدلال في كود قابل للتنفيذ للأجهزة المحمولة.

عادةً ما يتم تنفيذ الرسم البياني TensorFlow بواسطة وقت تشغيل TensorFlow. هذا يتطلب بعض الحمل الزائد لوقت التشغيل لتنفيذ كل عقدة في الرسم البياني. يؤدي هذا أيضًا إلى حجم ثنائي إجمالي أكبر ، نظرًا لأن الشفرة الخاصة بوقت تشغيل TensorFlow يجب أن تكون متاحة ، بالإضافة إلى الرسم البياني نفسه. لا يستخدم الكود القابل للتنفيذ الذي تم إنتاجه بواسطة tfcompile وقت تشغيل TensorFlow ، وله فقط تبعيات على النواة المستخدمة بالفعل في الحساب.

تم بناء المترجم فوق إطار عمل XLA. الكود الذي يربط TensorFlow بإطار عمل XLA موجود تحت tensorflow / compiler .

ماذا يفعل 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 macro لتجميع الرسم البياني الفرعي

تقوم هذه الخطوة بتحويل الرسم البياني إلى cc_library باستخدام ماكرو tf_library build. تتكون 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 الحجة، التي تدير الأداة. لمزيد من الأمثلة ، راجع tensorflow / compiler / aot / tests / BUILD .

الثوابت التي تظهر في الرسم البياني الفرعي المترجمة يتم تجميعها مباشرة في الكود المُنشأ. لتمرير الثوابت إلى الدالة المُولَّدة ، بدلاً من تجميعها ، ما عليك سوى تمريرها كخلاصات.

للحصول على تفاصيل حول tf_library build الماكرو ، راجع tfcompile.bzl .

للحصول على تفاصيل حول أداة tfcompile الأساسية ، راجع tfcompile_main.cc .

الخطوة 3: اكتب الكود لاستدعاء الرسم البياني الفرعي

تستخدم هذه الخطوة ملف الرأس ( test_graph_tfmatmul.h ) الذي تم إنشاؤه بواسطة ماكرو tf_library build في الخطوة السابقة لاستدعاء الكود الذي تم إنشاؤه. يوجد ملف الرأس في دليل bazel-bin المقابل لحزمة tf_library ، ويتم تسميته بناءً على سمة الاسم المحددة على ماكرو tf_library build. على سبيل المثال ، سيكون الرأس الذي تم إنشاؤه لـ 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 في مساحة الاسم foo::bar ، لأنها كانت فئة cpp_class المحددة في الماكرو tf_library . جميع الفئات التي تم إنشاؤها لها واجهة برمجة تطبيقات مماثلة ، مع الاختلاف الوحيد هو طرق التعامل مع الوسيطات الوسيطة والنتيجة. هذه الأساليب تختلف بناء على عدد وأنواع المخازن المؤقتة، التي تم تحديدها من قبل feed و fetch الحجج ل tf_library الماكرو.

هناك ثلاثة أنواع من مخازن تدار ضمن الفئة التي تم إنشاؤها: args تمثل المدخلات و results تمثل المخرجات، و temps يمثل المخازن المؤقتة المستخدمة داخليا لأداء الحساب. بشكل افتراضي ، يخصص كل مثيل من الفئة التي تم إنشاؤها ويدير كل هذه المخازن المؤقتة نيابة عنك. يمكن استخدام وسيطة مُنشئ AllocMode لتغيير هذا السلوك. يتم محاذاة كافة المخازن المؤقتة إلى حدود 64 بايت.

تعد فئة C ++ التي تم إنشاؤها مجرد غلاف حول رمز المستوى المنخفض الذي تم إنشاؤه بواسطة XLA.

مثال على استدعاء دالة مُولدة بناءً على 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 BUILD.

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