1. 程式人生 > >帶你深入AI(6)- 詳解bazel

帶你深入AI(6)- 詳解bazel

系列文章,請多關注
Tensorflow原始碼解析1 – 核心架構和原始碼結構
自然語言處理1 – 分詞
帶你深入AI(1) - 深度學習模型訓練痛點及解決方法
帶你深入AI(2)- 深度學習啟用函式,準確率,優化方法等總結
帶你深入AI(3)- 物體分類領域:AlexNet VGG Inception ResNet mobileNet
帶你深入AI(4)- 目標檢測領域:R-CNN,faster R-CNN,yolo,SSD, yoloV2
帶你深入AI(5)- 自然語言處理領域:RNN LSTM GRU
帶你深入AI(6)- 詳解bazel
帶你深入AI(7)- 深度學習重要Python庫

1 Bazel簡介

bazel是Google開源的一套編譯構建工具,廣泛應用於Google內部,包括TensorFlow專案。修改TensorFlow內部原始碼,需要使用bazel來編譯,故有必要了解下bazel。bazel優點很多,主要有

  1. 構建快。支援增量編譯。對依賴關係進行了優化,從而支援併發執行。
  2. 可構建多種語言。bazel可用來構建Java C++ Android ios等很多語言和框架,並支援mac windows linux等不同平臺
  3. 可伸縮。可處理任意大小的程式碼庫,可處理多個庫,也可以處理單個庫
  4. 可擴充套件。使用bazel擴充套件語言可支援新語言和新平臺。

2 Bazel專案結構

和Makefile一樣,使用bazel編譯也必須滿足它的專案結構要求。這也許是為什麼bazel還不夠普及的原因所在吧。bazel頂層,也就是根目錄下為工作區workspace,workspace下包含多個package,每個package又包含多個編譯目標target。

2.1 工作區workspace

要進行構建的檔案系統,根目錄下必須包含一個檔名為WORKSPACE的檔案,即使它內容為空。它指明瞭構建的根目錄。檔案系統中包括原始檔,標頭檔案,輸出目錄的符號連結等。WORKSPACE採用類似Python的語法,下面是TensorFlow原始碼根目錄下的WORKSPACE

workspace(name = "org_tensorflow")

http_archive(
    name = "io_bazel_rules_closure",
    sha256 = "6691c58a2cd30a86776dd9bb34898b041e37136f2dc7e24cadaeaf599c95c657",
    strip_prefix = "rules_closure-08039ba8ca59f64248bb3b6ae016460fe9c9914f",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/08039ba8ca59f64248bb3b6ae016460fe9c9914f.tar.gz",
        "https://github.com/bazelbuild/rules_closure/archive/08039ba8ca59f64248bb3b6ae016460fe9c9914f.tar.gz",  # 2018-01-16
    ],
)

load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")

closure_repositories()

load("//tensorflow:workspace.bzl", "tf_workspace")

# Uncomment and update the paths in these entries to build the Android demo.
android_sdk_repository(
    name = "androidsdk",
    api_level = 23,
#    # Ensure that you have the build_tools_version below installed in the
#    # SDK manager as it updates periodically.
    build_tools_version = "26.0.1",
#    # Replace with path to Android SDK on your system
    path = "/Users/yangyixie/Library/Android/sdk",
)
#
android_ndk_repository(
    name="androidndk",
    path="/Users/yangyixie/Library/Android/sdk/ndk-bundle",
#    # This needs to be 14 or higher to compile TensorFlow.
#    # Please specify API level to >= 21 to build for 64-bit
#    # archtectures or the Android NDK will automatically select biggest
#    # API level that it supports without notice.
#    # Note that the NDK version is not the API level.
    api_level=14,
)

# Please add all new TensorFlow dependencies in workspace.bzl.
tf_workspace()

new_http_archive(
    name = "inception_v1",
    build_file = "models.BUILD",
    sha256 = "7efe12a8363f09bc24d7b7a450304a15655a57a7751929b2c1593a71183bb105",
    urls = [
        "http://storage.googleapis.com/download.tensorflow.org/models/inception_v1.zip",
        "http://download.tensorflow.org/models/inception_v1.zip",
    ],
)

new_http_archive(
    name = "mobile_ssd",
    build_file = "models.BUILD",
    sha256 = "bddd81ea5c80a97adfac1c9f770e6f55cbafd7cce4d3bbe15fbeb041e6b8f3e8",
    urls = [
        "http://storage.googleapis.com/download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_android_export.zip",
        "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_android_export.zip",
    ],
)

new_http_archive(
    name = "mobile_multibox",
    build_file = "models.BUILD",
    sha256 = "859edcddf84dddb974c36c36cfc1f74555148e9c9213dedacf1d6b613ad52b96",
    urls = [
        "http://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1a.zip",
        "http://download.tensorflow.org/models/mobile_multibox_v1a.zip",
    ],
)

new_http_archive(
    name = "stylize",
    build_file = "models.BUILD",
    sha256 = "3d374a730aef330424a356a8d4f04d8a54277c425e274ecb7d9c83aa912c6bfa",
    urls = [
        "http://storage.googleapis.com/download.tensorflow.org/models/stylize_v1.zip",
        "http://download.tensorflow.org/models/stylize_v1.zip",
    ],
)

new_http_archive(
    name = "speech_commands",
    build_file = "models.BUILD",
    sha256 = "c3ec4fea3158eb111f1d932336351edfe8bd515bb6e87aad4f25dbad0a600d0c",
    urls = [
        "http://storage.googleapis.com/download.tensorflow.org/models/speech_commands_v0.01.zip",
        "http://download.tensorflow.org/models/speech_commands_v0.01.zip",
    ],
)

如上所示,語法類似Python,進行了多個方法呼叫,我們來看各個方法呼叫的含義。

http_archive:下載bazel檔案,然後解壓它,這個bazel 目錄檔案中必須包含BUILD檔案。上面的http_archive中指明瞭要下載io_bazel_rules_closure檔案,以及它的下載地址

new_http_archive: 下載檔案,然後解壓它,然後和其中包含的build_file一起建立bazel目錄

load:從.bzl檔案中載入一些內容,如上面從defs.bzl檔案中載入內容

android_sdk_repository:構建Android app時使用,指定Android sdk目錄

android_ndk_repository:構建Android app時使用,指定Android ndk目錄

2.2 包package

一個WORKSPACE工作區下可以包括多個包package,每個package可以實現一個子模組,從而讓各個模組進行解耦。每個package下必須包含一個BUILD檔案,它指定了package的編譯構建規則。由於TensorFlow原始碼是C++檔案,故本文只講解C++下的BUILD檔案,以及它的構建規則。

我們先列出TensorFlow原始碼中的一個BUILD檔案,然後講解BUILD檔案中的各個規則含義。

# Description:
# TensorFlow is a computational framework, primarily for use in machine
# learning applications.

# 通用方法,定義的值會作用到下面的每個子rule中。default_visibility指定了這個包的預設可見規則。可見的情況下才能被其他package呼叫。
package(
    default_visibility = ["//visibility:public"],
)

# 預設的license
licenses(["notice"])  # Apache 2.0

# 通用方法,載入.bzl檔案
load(
    "//tensorflow:tensorflow.bzl",
    "tf_cc_test",
    "tf_cc_binary",
    "tf_copts",
    "tf_gen_op_wrappers_cc",
    "cc_library_with_android_deps",
)

# c++庫檔案,name指定了編譯為庫檔案後的檔名,srcs和hdrs指定原始檔和標頭檔案,deps指定需要依賴的其他檔案
cc_library(
    name = "gradient_checker",
    srcs = ["framework/gradient_checker.cc"],
    hdrs = ["framework/gradient_checker.h"],
    deps = [
        ":cc_ops",
        ":client_session",
        ":gradients",
        ":ops",
        ":scope",
        "//tensorflow/core:framework",
        "//tensorflow/core:lib",
        "//tensorflow/core:lib_internal",
    ],
)

# c++測試檔案
tf_cc_test(
    name = "gradients_array_grad_test",
    srcs = ["gradients/array_grad_test.cc"],
    deps = [
        ":array_grad",
        ":cc_ops",
        ":cc_ops_internal",
        ":grad_op_registry",
        ":grad_testutil",
        ":gradient_checker",
        ":testutil",
        "//tensorflow/core:test",
        "//tensorflow/core:test_main",
        "//tensorflow/core:testlib",
    ],
)

# c++編譯目標檔案,為一個二進位制可執行檔案。name必須唯一,srcs指定了原始檔,linkopts指定了連結規則,deps指定了依賴檔案
tf_cc_binary(
    name = "tutorials_example_trainer",
    srcs = ["tutorials/example_trainer.cc"],
    copts = tf_copts(),
    linkopts = select({
        "//tensorflow:windows": [],
        "//tensorflow:windows_msvc": [],
        "//tensorflow:darwin": [
            "-lm",
            "-lpthread",
        ],
        "//tensorflow:ios": [
            "-lm",
            "-lpthread",
        ],
        "//conditions:default": [
            "-lm",
            "-lpthread",
            "-lrt",
        ],
    }),
    deps = [
        ":cc_ops",
        "//tensorflow/core:core_cpu",
        "//tensorflow/core:framework",
        "//tensorflow/core:lib",
        "//tensorflow/core:protos_all_cc",
        "//tensorflow/core:tensorflow",
    ],
)

# 為多個編譯目標target指定一個名字,glob是一個幫助函式,指定了目錄中哪些檔案會include,哪些會exclude。visibility指定了target的可見性,也就是可以被哪些package呼叫
filegroup(
    name = "all_files",
    srcs = glob(
        ["**/*"],
        exclude = [
            "**/METADATA",
            "**/OWNERS",
        ],
    ),
    visibility = ["//tensorflow:__subpackages__"],
)

BUILD檔案也是採用的類似Python的語法,它定義了編譯規則,lib依賴等各項規則。其中有些命令專屬於BUILD檔案,有些則是bazel語法通用的,如WORKSPACE檔案中也可以使用。主要方法呼叫如下

tf_cc_binary:目標檔案編譯規則,為一個二進位制可執行檔案。name必須唯一,srcs指定了原始檔,linkopts指定了連結規則,deps指定了依賴檔案

cc_library:庫檔案編譯規則,name指定了編譯為庫檔案後的檔名,srcs和hdrs指定原始檔和標頭檔案,deps指定需要依賴的其他檔案

tf_cc_test:測試檔案規則

package:通用方法,定義的值會作用到下面的每個子rule中。default_visibility指定了這個包的預設可見規則。可見的情況下才能被其他package呼叫。

licenses:通用方法,預設的license

load:通用方法,載入.bzl檔案

filegroup:通用方法,為多個編譯目標target指定一個名字,glob是一個幫助函式,指定了目錄中哪些檔案會include,哪些會exclude。visibility指定了target的可見性,也就是可以被哪些package呼叫

其他一些常用方法可以參看bazel文件 https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary.linkopts

2.2.1 標籤

包的名稱叫做標籤,用來標識一個包package。標籤示例如下

//my/app/main:app_binary

標籤由兩部分組成,一部分為包名my/app/main, 一部分為包構建的目標名app_binary。每個標籤用來唯一標示包構建的目標,從而在被其他包使用時可以標識出來。同一個包下,標籤可以省略包名部分,如:app_binary表示同一個包下的目標。不同包之間,則千萬不能省略包名。

2.3 目標

包package是一個容器,組成它的元素稱為目標,分為檔案和規則。檔案分為兩種,一種為程式設計師寫的原始碼,一種為構建工具生成的檔案。規則定義瞭如何利用輸入來構建得到輸出,如上面的BUILD。輸入一般是原始檔,庫檔案等,輸出則一般是生成的構建目標檔案。

3 安裝和使用示例

bazel安裝參見 https://docs.bazel.build/versions/master/install.html

bazel的入門例項參見 https://docs.bazel.build/versions/master/tutorial/cpp.html#specify-multiple-build-targets

系列文章,請多關注
Tensorflow原始碼解析1 – 核心架構和原始碼結構
自然語言處理1 – 分詞
帶你深入AI(1) - 深度學習模型訓練痛點及解決方法
帶你深入AI(2)- 深度學習啟用函式,準確率,優化方法等總結
帶你深入AI(3)- 物體分類領域:AlexNet VGG Inception ResNet mobileNet
帶你深入AI(4)- 目標檢測領域:R-CNN,faster R-CNN,yolo,SSD, yoloV2
帶你深入AI(5)- 自然語言處理領域:RNN LSTM GRU
帶你深入AI(6)- 詳解bazel
帶你深入AI(7)- 深度學習重要Python庫