TensorFlow技術(shù)內(nèi)幕(二):編譯與安裝

本篇中介紹一下TensorFlow的安裝。TensorFlow的安裝分為安裝包安裝和編譯安裝.

一般的用戶使用安裝包安裝就可以了,并且安裝包的方式簡單方便,具體又分為基于pip安裝、基于docker安裝、基于VirtualEnv的安裝和基于Anaconda的安裝,基本的過程都是先準(zhǔn)備好Python環(huán)境,然后直接通過Pip(python的包管理器)直接下載安裝TensorFlow的Python包,比較簡單,這里就不再贅述了,可自行g(shù)oogle或參考這兩篇文章,寫的十分的詳細(xì):wiki, doc

基于編譯源碼的安裝方式在用戶找不到自己平臺合適的安裝包、或則是想要深入學(xué)習(xí)TensorFlow實現(xiàn)的情況下使用。這里我們詳細(xì)介紹一下基于源碼的安裝方式。

TensorFlow官網(wǎng)也有一篇講解源碼安裝的文章,也值得參考一下:源碼安裝

那么本文跟這些文章的區(qū)別在哪呢?本人力求做到跟官網(wǎng)的手冊互補,本文中我會將重點放在原理的講解上,而這些參考資料的重點都是在實際動手操作上。

至于閱讀順序完全在于讀者的喜好,可以對照這些操作手冊先實踐一遍,然后回過頭來看這篇講解;也可以先看我這偏講解,然后再實踐。

環(huán)境準(zhǔn)備

編譯TensorFlow工程的時候,有很多可選功能可以選擇是否開啟,有是否需要GPU支持,還有是否需要支持HDFS,是否需要OpenGL,Google Cloud,XLA優(yōu)化等等。用戶選擇的開啟的功能越多,TensorFlow的依賴項就越多,環(huán)境準(zhǔn)備就越復(fù)雜些。

1、首先我們需要選擇平臺和操作系統(tǒng):
Ubuntu和max OS X是官方推薦的兩個平臺,本篇中我們選擇Ubuntu 16.04 64位作為編譯平臺。

2、安裝構(gòu)建工具Bazel:
Bazel是一個開源的構(gòu)建系統(tǒng),同樣來自于google,在google內(nèi)部使用的也比較廣泛。

構(gòu)建系統(tǒng)的需求是隨著軟件規(guī)模的增大而提出的。在軟件規(guī)模很小的時候,我們可以手動調(diào)用gcc編譯和鏈接生成目標(biāo)文件。但是隨著軟件規(guī)模的增大,這種方式顯然很低效,于是出現(xiàn)的構(gòu)建工具,我們可以定義構(gòu)建目標(biāo)的規(guī)則文件,然后由構(gòu)建工具來解析這個規(guī)則文件,調(diào)用gcc來編譯何生成目標(biāo)文件。隨著軟件規(guī)模的進(jìn)一步擴(kuò)大,出現(xiàn)了跨平臺的需求。這時候構(gòu)建工具也提供了根據(jù)不同的平臺定義不同的構(gòu)建規(guī)則的功能。

類似的構(gòu)建工具還有Make, Maven, Gradle, GPY,GN(chromium目前采用的構(gòu)建工具)等等。

現(xiàn)代構(gòu)建工具的功能越來越強(qiáng)大,很多都支持多平臺,多語言,遠(yuǎn)程依賴等等。

Bazel的安裝也很簡單,詳細(xì)參考:安裝Bazel

我們通過一個例子來測試和熟悉一下Bazel的使用,例子是Bazel官方提供的。

首先來獲取例子代碼:

git clone https://github.com/bazelbuild/examples/

可以看到目錄 examples/cpp-tutorial 結(jié)構(gòu)如下:

examples
└── cpp-tutorial
    ├──stage1
    │  ├── main
    │  │   ├── BUILD
    │  │   └── hello-world.cc
    │  └── WORKSPACE
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE

來看一下BUILD文件的內(nèi)容,cpp-tutorial/stage1/main/BUILD如下:

# 通過cc_binary規(guī)則定義了一個binary目標(biāo),
# 目標(biāo)名稱為 hello-world,源文件是 hello-world.cc.
cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

構(gòu)建hello-world的方式也簡單,執(zhí)行如下命令:

bazel build //main:hello-world

cpp-tutorial/stage2/main/BUILD內(nèi)容如下:

# 通過cc_library規(guī)則定義了一個library目標(biāo),
# 目標(biāo)名稱為 hello-greet,源文件是 hello-greet.cc,
# hello-greet.h
cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)


# 通過cc_binary規(guī)則定義了一個binary目標(biāo),
# 目標(biāo)名稱為 hello-world,源文件是 hello-world.cc.
# 并且依賴包內(nèi)目標(biāo)hello-greet
cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

構(gòu)建方式?jīng)]變化:

bazel build //main:hello-world

cpp-tutorial/stage3/main/BUILD內(nèi)容如下:

# 通過cc_library規(guī)則定義了一個library目標(biāo),
# 目標(biāo)名稱為 hello-greet,源文件是 hello-greet.cc,
# hello-greet.h
cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

# 通過cc_binary規(guī)則定義了一個binary目標(biāo),
# 目標(biāo)名稱為 hello-world,源文件是 hello-world.cc.
# 并且依賴包內(nèi)目標(biāo)hello-greet和包lib下的目標(biāo)//lib:hello-time
cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "http://lib:hello-time",
    ],
)

在看一下包lib下的BUILD文件:

# 通過cc_library規(guī)則定義了一個library目標(biāo),
# 目標(biāo)名稱為 hello-time,源文件是 hello-time.cc,
# hello-time.h
cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["http://main:__pkg__"],
)

的確定義了一個目標(biāo)hello-time,并且設(shè)置了main包可見。

構(gòu)建方式依然沒變:

bazel build //main:hello-world

3、安裝Python以及依賴項:

主要的Python依賴有這幾項

  • numpy:這是 Python 中常用的科學(xué)計算包,支持很多矩陣運算,并提供了高緯運算的優(yōu)化算法。

  • dev:這是 Python 開發(fā)包,用于向 Python 添加擴(kuò)展程序;其中包括了一些用C/Java/C#等語言編寫的python擴(kuò)展在編譯的時候依賴的頭文件,靜態(tài)庫等文件。TensorFlow不完全由Python寫成,核心執(zhí)行模塊是有C++,CUDA寫成的,因此需要此包。

  • pip:Python 軟件包管理器;提供了對 Python 包的查找、下載、安裝、卸載的功能。

  • wheel:用于管理 wheel (.whl) 格式的 Python 壓縮包。

根據(jù)你的Python版本,安裝方式稍有不同,詳細(xì)安裝方式,參考:安裝python依賴

4、安裝GPU依賴:

  • Nvidia顯卡

  • Nvidia顯卡驅(qū)動

  • Cuda ToolKit : 是Nvidia推出的使用GPU資源進(jìn)行通用計算的SDK,TensorFlow的核心計算層通過cuda接口,驅(qū)動顯卡的GPU進(jìn)行計算。CUDA安裝包一般會集成了顯卡驅(qū)動。

  • cuDNN: 是Nvidia推出的深度學(xué)習(xí)中CNN和RNN的高度優(yōu)化的實現(xiàn)。因為底層使用了很多先進(jìn)的技術(shù)何接口沒有對外開源,因此性能高很多。

安裝方式不再贅述,參考:安裝GPU依賴

從源碼安裝

目前為止,我們的準(zhǔn)備工作就完成了,可以開始編譯工程了。接下來的工作就比較簡單了,基本流程是 git clone 獲取源碼、執(zhí)行 configure 腳本配置編譯選項、執(zhí)行bazel build命令構(gòu)建目標(biāo)、執(zhí)行目標(biāo)腳本生成TensorFlow安裝包、pip安裝目標(biāo)TensorFlow安裝包,具體操作不再贅述,參考官網(wǎng)手冊:編譯TensorFlow,我們來重點理解一下這幾個問題:

1、configure腳本是如何配置編譯選項?

編譯之前,需要執(zhí)行configure腳本,腳本會提示用戶配置一些編譯選項,例如是否支持CUDA,OpenGL,HDFS,Google Cloud等等。那么這些配置選項是如果一項后續(xù)的編譯的呢?

以python環(huán)境配置為例看一下配置的原理;我們來看一下腳本configure中setup_python函數(shù):

function setup_python {
  ## Set up python-related environment settings:
  ##
  ## 這個while循環(huán)用來設(shè)置Python可執(zhí)行文件的路徑PYTHON_BIN_PATH,
  ## 使用which命令自動查找python的路徑作為可選項,用戶可以自己指定路徑
  ##
  while true; do
    fromuser=""
    if [ -z "$PYTHON_BIN_PATH" ]; then
      default_python_bin_path=$(which python || which python3 || true)
      read -p "Please specify the location of python. [Default is $default_python_bin_path]: " PYTHON_BIN_PATH
      fromuser="1"
      if [ -z "$PYTHON_BIN_PATH" ]; then
        PYTHON_BIN_PATH=$default_python_bin_path
      fi
    fi
    if [ -e "$PYTHON_BIN_PATH" ]; then
      break
    fi
    echo "Invalid python path. ${PYTHON_BIN_PATH} cannot be found" 1>&2
    if [ -z "$fromuser" ]; then
      exit 1
    fi
    PYTHON_BIN_PATH=""
    # Retry
  done
  
  
  ##
  ## 下面的if邏輯用來設(shè)置PYTHON_LIB_PATH
  ##
  if [ -z "$PYTHON_LIB_PATH" ]; then
    # Split python_path into an array of paths, this allows path containing spaces
    IFS=',' read -r -a python_lib_path <<< "$(python_path)"

    if [ 1 = "$USE_DEFAULT_PYTHON_LIB_PATH" ]; then
      PYTHON_LIB_PATH=${python_lib_path[0]}
      echo "Using python library path: $PYTHON_LIB_PATH"

    else
      echo "Found possible Python library paths:"
      for x in "${python_lib_path[@]}"; do
        echo "  $x"
      done
      set -- "${python_lib_path[@]}"
      echo "Please input the desired Python library path to use.  Default is [$1]"
      read b || true
      if [ "$b" == "" ]; then
        PYTHON_LIB_PATH=${python_lib_path[0]}
        echo "Using python library path: $PYTHON_LIB_PATH"
      else
        PYTHON_LIB_PATH="$b"
      fi
    fi
  fi

  ##
  ## 檢查PYTHON_BIN_PATH路徑是否有效,無效則結(jié)束配置腳本
  ##
  if [ ! -x "$PYTHON_BIN_PATH" ]  || [ -d "$PYTHON_BIN_PATH" ]; then
    echo "PYTHON_BIN_PATH is not executable.  Is it the python binary?"
    exit 1
  fi

  local python_major_version
  python_major_version=$("${PYTHON_BIN_PATH}" -c 'from __future__ import print_function; import sys; print(sys.version_info[0]);' | head -c1)
  if [ -z "$python_major_version" ]; then
    echo -e "\n\nERROR: Problem getting python version.  Is $PYTHON_BIN_PATH the correct python binary?"
    exit 1
  fi

  # Convert python path to Windows style before writing into bazel.rc
  if is_windows; then
    PYTHON_BIN_PATH="$(cygpath -m "$PYTHON_BIN_PATH")"
    PYTHON_LIB_PATH="$(cygpath -m "$PYTHON_LIB_PATH")"
  fi
  
  
  ##
  ## 接下來的邏輯是將配置固化到磁盤,涉及兩個磁盤文件.tf_configure.bazelrc和
  ## tools/python_bin_path.sh,后面我們講介紹這兩個文件的作用。
  ## 

  # Set-up env variables used by python_configure.bzl
  write_action_env_to_bazelrc "PYTHON_BIN_PATH" "$PYTHON_BIN_PATH"
  write_action_env_to_bazelrc "PYTHON_LIB_PATH" "$PYTHON_LIB_PATH"
  write_to_bazelrc "build --define PYTHON_BIN_PATH=\"$PYTHON_BIN_PATH\""
  write_to_bazelrc "build --define PYTHON_LIB_PATH=\"$PYTHON_LIB_PATH\""
  write_to_bazelrc "build --force_python=py$python_major_version"
  write_to_bazelrc "build --host_force_python=py$python_major_version"
  write_to_bazelrc "build --python${python_major_version}_path=\"$PYTHON_BIN_PATH\""
  write_to_bazelrc "test --force_python=py$python_major_version"
  write_to_bazelrc "test --host_force_python=py$python_major_version"
  write_to_bazelrc "test --define PYTHON_BIN_PATH=\"$PYTHON_BIN_PATH\""
  write_to_bazelrc "test --define PYTHON_LIB_PATH=\"$PYTHON_LIB_PATH\""
  write_to_bazelrc "run --define PYTHON_BIN_PATH=\"$PYTHON_BIN_PATH\""
  write_to_bazelrc "run --define PYTHON_LIB_PATH=\"$PYTHON_LIB_PATH\""

  # Write tools/python_bin_path.sh
  echo "export PYTHON_BIN_PATH=\"$PYTHON_BIN_PATH\"" > tools/python_bin_path.sh
}

這里面使用了兩個函數(shù)來保存配置,如下:

function write_to_bazelrc() {
  echo "$1" >> .tf_configure.bazelrc
}

function write_action_env_to_bazelrc() {
  write_to_bazelrc "build --action_env $1=\"$2\""
}

如果執(zhí)行成功,會生成文件.tf_configure.bazelrc,內(nèi)容如下:

build --action_env PYTHON_BIN_PATH="/usr/bin/python"
build --action_env PYTHON_LIB_PATH="/usr/local/lib/python2.7/dist-packages"
build --define PYTHON_BIN_PATH="/usr/bin/python"
build --define PYTHON_LIB_PATH="/usr/local/lib/python2.7/dist-packages"
build --force_python=py2
build --host_force_python=py2
build --python2_path="/usr/bin/python"
test --force_python=py2
test --host_force_python=py2
test --define PYTHON_BIN_PATH="/usr/bin/python"
test --define PYTHON_LIB_PATH="/usr/local/lib/python2.7/dist-packages"
run --define PYTHON_BIN_PATH="/usr/bin/python"
run --define PYTHON_LIB_PATH="/usr/local/lib/python2.7/dist-packages"
build:opt --cxxopt=-march=native --copt=-march=native
build --action_env TF_NEED_CUDA="0"
build --action_env TF_NEED_OPENCL="0"

這個文件在后面的bazel編譯中會用到??梢钥闯?,這其中記錄的是編譯時期需要傳遞給bazel的參數(shù)信息。根據(jù)配置的不同,用戶自己機(jī)器上的文件內(nèi)容可以會差異,屬于正?,F(xiàn)象。

2、編譯目標(biāo)是什么?又是如何構(gòu)建的呢?

僅支持 CPU 的情況下,構(gòu)建的目標(biāo)的命令如下:

$ bazel build --config=opt //tensorflow/tools/pip_package:build_pip_package

支持 GPU 的情況下,構(gòu)建的目標(biāo)的命令如下:

$ bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package 

我們來看一下構(gòu)建的目標(biāo)build_pip_package,回憶前面bazel的例子,找到定義它的BUILD文件 tensorflow/tools/pip_package/BUILD, 目標(biāo)的定義如下:

sh_binary(
    name = "build_pip_package",
    srcs = ["build_pip_package.sh"],
    data = select({
        "http://tensorflow:windows": [":simple_console_for_windows"],
        "http://tensorflow:windows_msvc": [":simple_console_for_windows"],
        "http://conditions:default": [
            ":licenses",
            "MANIFEST.in",
            "README",
            "setup.py",
            ":included_headers",
            ":simple_console",
            "http://tensorflow:tensorflow_py",
            "http://tensorflow/contrib/graph_editor:graph_editor_pip",
            "http://tensorflow/contrib/keras:keras",
            "http://tensorflow/contrib/labeled_tensor:labeled_tensor_pip",
            "http://tensorflow/contrib/ndlstm:ndlstm",
            "http://tensorflow/contrib/nn:nn_py",
            "http://tensorflow/contrib/session_bundle:session_bundle_pip",
            "http://tensorflow/contrib/signal:signal_py",
            "http://tensorflow/contrib/slim:slim",
            "http://tensorflow/contrib/slim/python/slim/data:data_pip",
            "http://tensorflow/contrib/slim/python/slim/nets:nets_pip",
            "http://tensorflow/contrib/tpu:tpu_estimator",
            "http://tensorflow/contrib/tpu:tpu_helper_library",
            "http://tensorflow/contrib/tpu:tpu_py",
            "http://tensorflow/contrib/specs:specs",
            "http://tensorflow/contrib/tensor_forest:init_py",
            "http://tensorflow/contrib/tensor_forest/hybrid:hybrid_pip",
            "http://tensorflow/contrib/predictor:predictor_pip",
            "http://tensorflow/examples/tutorials/mnist:package",
            "http://tensorflow/python:distributed_framework_test_lib",
            "http://tensorflow/python:meta_graph_testdata",
            "http://tensorflow/python:util_example_parser_configuration",
            "http://tensorflow/python/debug:debug_pip",
            "http://tensorflow/python/saved_model:saved_model",
            "http://tensorflow/python/tools:tools_pip",
        ],
    }) + if_mkl(["http://third_party/mkl:intel_binary_blob"]),
)

我們遇到了bazel的新的規(guī)則sh_binary以及一個select函數(shù),我們來一下它們的定義:

sh_bianry用來定義一個可執(zhí)行的Bourne Shell腳本目標(biāo),name表示目標(biāo)的名字,srcs是腳本文件,必須是可執(zhí)行的腳本,腳本運行時需要的其他文件由data屬性定義,目標(biāo)構(gòu)建完成后,這些被依賴項都會在目標(biāo)的runfiles目錄內(nèi)。

select函數(shù)根據(jù)bazel的command-line的參數(shù)返回不同的結(jié)果。

綜合起來看,目標(biāo) build_pip_package 的可執(zhí)行腳本是build_pip_package.sh,data 的屬性值取決于bazel的命令行參數(shù)。我們先忽略windows平臺下的取值,看到 build_pip_package 目標(biāo)依賴 //tensorflow:tensorflow_py、//tensorflow/contrib/graph_editor:graph_editor_pip 等眾多目標(biāo),這里暫時先不去一一細(xì)看這些被依賴項目。在構(gòu)建 build_pip_package 目標(biāo)的時候,bazel會遞歸的構(gòu)建所有的被依賴目標(biāo)。

接下來,我們來看下shell腳本build_pip_package.sh,它的主要工作在main函數(shù)里完成:

function main() {

  ## 
  ## 下面的代碼做參數(shù)檢查,用戶執(zhí)行此腳本的時候需要提供一個目標(biāo)文件夾路徑,
  ## 作為最后whl安裝包生成的路徑
  ## 
  if [ $# -lt 1 ] ; then
    echo "No destination dir provided"
    exit 1
  fi

  DEST=$1
  TMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX)

  GPU_FLAG=""
  while true; do
    if [[ "$1" == "--gpu" ]]; then
      GPU_FLAG="--project_name tensorflow_gpu"
    fi
    shift

    if [[ -z "$1" ]]; then
      break
    fi
  done

  echo $(date) : "=== Using tmpdir: ${TMPDIR}"

  if [ ! -d bazel-bin/tensorflow ]; then
    echo "Could not find bazel-bin.  Did you run from the root of the build tree?"
    exit 1
  fi
  
  
  ##
  ## 下面的代碼是在做文件拷貝,將編譯生成的文件拷貝到目標(biāo)路徑中。
  ## 不同系統(tǒng)可能源目錄的結(jié)構(gòu)不一樣,再有就是bazel的版本更新,也導(dǎo)致
  ## 新舊版本的源路徑結(jié)構(gòu)不太一樣,等等原因;這里的代碼兼容了各種源目
## 錄的結(jié)構(gòu)。runfiles目錄中就是之前所有依賴生成文件會出現(xiàn)的位置。
  ##

  if is_windows; then
    rm -rf ./bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip
    mkdir -p ./bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip
    echo "Unzipping simple_console_for_windows.zip to create runfiles tree..."
    unzip -o -q ./bazel-bin/tensorflow/tools/pip_package/simple_console_for_windows.zip -d ./bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip
    echo "Unzip finished."
    # runfiles structure after unzip the python binary
    cp -R \
      bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip/runfiles/org_tensorflow/tensorflow \
      "${TMPDIR}"
    mkdir "${TMPDIR}/external"
    # Note: this makes an extra copy of org_tensorflow.
    cp_external \
      bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip/runfiles \
      "${TMPDIR}/external"
    RUNFILES=bazel-bin/tensorflow/tools/pip_package/simple_console_for_window_unzip/runfiles/org_tensorflow
  elif [ ! -d bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow ]; then
    # Really old (0.2.1-) runfiles, without workspace name.
    cp -R \
      bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/tensorflow \
      "${TMPDIR}"
    mkdir "${TMPDIR}/external"
    cp_external \
      bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/external \
      "${TMPDIR}/external"
    RUNFILES=bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles
    # Copy MKL libs over so they can be loaded at runtime
    if [ -d bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl ]; then
      mkdir "${TMPDIR}/_solib_k8"
        cp -R \
            bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl \
        "${TMPDIR}/_solib_k8"
    fi
  else
    if [ -d bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/external ]; then
      # Old-style runfiles structure (--legacy_external_runfiles).
      cp -R \
        bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/tensorflow \
        "${TMPDIR}"
      mkdir "${TMPDIR}/external"
      cp_external \
        bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/external \
        "${TMPDIR}/external"
      # Copy MKL libs over so they can be loaded at runtime
      if [ -d bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl ]; then
        mkdir "${TMPDIR}/_solib_k8"
        cp -R \
          bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl \
          "${TMPDIR}/_solib_k8"
      fi
    else
      # New-style runfiles structure (--nolegacy_external_runfiles).
      cp -R \
        bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/tensorflow \
        "${TMPDIR}"
      mkdir "${TMPDIR}/external"
      # Note: this makes an extra copy of org_tensorflow.
      cp_external \
        bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles \
        "${TMPDIR}/external"
      # Copy MKL libs over so they can be loaded at runtime
      if [ -d bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl ]; then
        mkdir "${TMPDIR}/_solib_k8"
            cp -R \
                bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/_solib_k8/_U_S_Sthird_Uparty_Smkl_Cintel_Ubinary_Ublob___Uthird_Uparty_Smkl \
          "${TMPDIR}/_solib_k8"
      fi
    fi
    RUNFILES=bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow
  fi

  # protobuf pip package doesn't ship with header files. Copy the headers
  # over so user defined ops can be compiled.
  mkdir -p ${TMPDIR}/google
  mkdir -p ${TMPDIR}/third_party
  pushd ${RUNFILES%org_tensorflow}
  for header in $(find protobuf -name \*.h); do
    mkdir -p "${TMPDIR}/google/$(dirname ${header})"
    cp "$header" "${TMPDIR}/google/$(dirname ${header})/"
  done
  popd
  cp -R $RUNFILES/third_party/eigen3 ${TMPDIR}/third_party
  
  
  #
  # 下面的代碼拷貝Python的whl格式的安裝包
  # 的幾個必須文件,MANIFEST.in, README, setup.py
  #
  cp tensorflow/tools/pip_package/MANIFEST.in ${TMPDIR}
  cp tensorflow/tools/pip_package/README ${TMPDIR}
  cp tensorflow/tools/pip_package/setup.py ${TMPDIR}

  # Before we leave the top-level directory, make sure we know how to
  # call python.
  source tools/python_bin_path.sh


  # 
  # 最后,下面的代碼調(diào)用Python生成whl格式的包文件
  # 
  pushd ${TMPDIR}
  rm -f MANIFEST
  echo $(date) : "=== Building wheel"
  "${PYTHON_BIN_PATH:-python}" setup.py bdist_wheel ${GPU_FLAG} >/dev/null
  mkdir -p ${DEST}
  cp dist/* ${DEST}
  popd
  rm -rf ${TMPDIR}
  echo $(date) : "=== Output wheel file is in: ${DEST}"
}

看得出來,build_pip_package.sh的腳本就是將我們的編譯結(jié)果打包成一個wheel格式的python包。

前面構(gòu)建完腳本目標(biāo)后,就可以執(zhí)行腳本生成wheel包:

$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

最后我們可以用pip安裝生成的wheel包:

$ sudo pip install /tmp/tensorflow_pkg/tensorflow-1.6.0-py2-none-any.whl

安裝完測試

為了檢查安轉(zhuǎn)是否完成,可以執(zhí)行一些測試代碼,下面的測試用例來自官網(wǎng),來看一下:

調(diào)用 Python:

$ python

在 Python 交互式 shell 中輸入以下幾行簡短的程序代碼:

# Python
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

如果系統(tǒng)輸出以下內(nèi)容,則說明順利完成:

Hello, TensorFlow!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容