带你深入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库

你可能感兴趣的:(深度学习)