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

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

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

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

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

那么本文跟這些文章的區(qū)別在哪呢禀崖?本人力求做到跟官網(wǎng)的手冊(cè)互補(bǔ)衩辟,本文中我會(huì)將重點(diǎn)放在原理的講解上螟炫,而這些參考資料的重點(diǎn)都是在實(shí)際動(dòng)手操作上。

至于閱讀順序完全在于讀者的喜好艺晴,可以對(duì)照這些操作手冊(cè)先實(shí)踐一遍昼钻,然后回過(guò)頭來(lái)看這篇講解;也可以先看我這偏講解封寞,然后再實(shí)踐然评。

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

編譯TensorFlow工程的時(shí)候,有很多可選功能可以選擇是否開啟狈究,有是否需要GPU支持碗淌,還有是否需要支持HDFS,是否需要OpenGL抖锥,Google Cloud亿眠,XLA優(yōu)化等等。用戶選擇的開啟的功能越多磅废,TensorFlow的依賴項(xiàng)就越多纳像,環(huán)境準(zhǔn)備就越復(fù)雜些。

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

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

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

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

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

Bazel的安裝也很簡(jiǎn)單沐批,詳細(xì)參考:安裝Bazel

我們通過(guò)一個(gè)例子來(lái)測(cè)試和熟悉一下Bazel的使用纫骑,例子是Bazel官方提供的。

首先來(lái)獲取例子代碼:

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

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

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

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

bazel build //main:hello-world

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

# 通過(guò)cc_library規(guī)則定義了一個(gè)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"],
)


# 通過(guò)cc_binary規(guī)則定義了一個(gè)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)容如下:

# 通過(guò)cc_library規(guī)則定義了一個(gè)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"],
)

# 通過(guò)cc_binary規(guī)則定義了一個(gè)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文件:

# 通過(guò)cc_library規(guī)則定義了一個(gè)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__"],
)

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

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

bazel build //main:hello-world

3、安裝Python以及依賴項(xiàng):

主要的Python依賴有這幾項(xiàng)

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

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

  • pip:Python 軟件包管理器;提供了對(duì) Python 包的查找链嘀、下載萌狂、安裝、卸載的功能怀泊。

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

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

4、安裝GPU依賴:

  • Nvidia顯卡

  • Nvidia顯卡驅(qū)動(dòng)

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

  • cuDNN: 是Nvidia推出的深度學(xué)習(xí)中CNN和RNN的高度優(yōu)化的實(shí)現(xiàn)。因?yàn)榈讓邮褂昧撕芏嘞冗M(jìn)的技術(shù)何接口沒有對(duì)外開源泊窘,因此性能高很多熄驼。

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

從源碼安裝

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

1仰禀、configure腳本是如何配置編譯選項(xiàng)?

編譯之前蚕愤,需要執(zhí)行configure腳本答恶,腳本會(huì)提示用戶配置一些編譯選項(xiàng),例如是否支持CUDA萍诱,OpenGL悬嗓,HDFS,Google Cloud等等裕坊。那么這些配置選項(xiàng)是如果一項(xiàng)后續(xù)的編譯的呢烫扼?

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

function setup_python {
  ## Set up python-related environment settings:
  ##
  ## 這個(gè)while循環(huán)用來(lái)設(shè)置Python可執(zhí)行文件的路徑PYTHON_BIN_PATH碍庵,
  ## 使用which命令自動(dòng)查找python的路徑作為可選項(xiàng)映企,用戶可以自己指定路徑
  ##
  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邏輯用來(lái)設(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路徑是否有效,無(wú)效則結(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
  
  
  ##
  ## 接下來(lái)的邏輯是將配置固化到磁盤静浴,涉及兩個(gè)磁盤文件.tf_configure.bazelrc和
  ## tools/python_bin_path.sh堰氓,后面我們講介紹這兩個(gè)文件的作用。
  ## 

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

這里面使用了兩個(gè)函數(shù)來(lái)保存配置苹享,如下:

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

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

如果執(zhí)行成功双絮,會(huì)生成文件.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"

這個(gè)文件在后面的bazel編譯中會(huì)用到得问《谂剩可以看出,這其中記錄的是編譯時(shí)期需要傳遞給bazel的參數(shù)信息宫纬。根據(jù)配置的不同焚挠,用戶自己機(jī)器上的文件內(nèi)容可以會(huì)差異,屬于正忱焐В現(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 

我們來(lái)看一下構(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以及一個(gè)select函數(shù)剃允,我們來(lái)一下它們的定義:

sh_bianry用來(lái)定義一個(gè)可執(zhí)行的Bourne Shell腳本目標(biāo),name表示目標(biāo)的名字,srcs是腳本文件斥废,必須是可執(zhí)行的腳本椒楣,腳本運(yùn)行時(shí)需要的其他文件由data屬性定義,目標(biāo)構(gòu)建完成后营袜,這些被依賴項(xiàng)都會(huì)在目標(biāo)的runfiles目錄內(nèi)撒顿。

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

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

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

function main() {

  ## 
  ## 下面的代碼做參數(shù)檢查,用戶執(zhí)行此腳本的時(shí)候需要提供一個(gè)目標(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目錄中就是之前所有依賴生成文件會(huì)出現(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格式的安裝包
  # 的幾個(gè)必須文件芋肠,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}"
}

看得出來(lái),build_pip_package.sh的腳本就是將我們的編譯結(jié)果打包成一個(gè)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

安裝完測(cè)試

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

調(diào)用 Python:

$ python

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

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

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

Hello, TensorFlow!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帮孔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌文兢,老刑警劉巖晤斩,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異姆坚,居然都是意外死亡澳泵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門兼呵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兔辅,“玉大人,你說(shuō)我怎么就攤上這事击喂∥Γ” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵懂昂,是天一觀的道長(zhǎng)介时。 經(jīng)常有香客問(wèn)我,道長(zhǎng)凌彬,這世上最難降的妖魔是什么沸柔? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮铲敛,結(jié)果婚禮上褐澎,老公的妹妹穿的比我還像新娘。我一直安慰自己原探,他們只是感情好乱凿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咽弦,像睡著了一般徒蟆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上型型,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天段审,我揣著相機(jī)與錄音,去河邊找鬼闹蒜。 笑死寺枉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绷落。 我是一名探鬼主播姥闪,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼砌烁!你這毒婦竟也來(lái)了筐喳?” 一聲冷哼從身側(cè)響起催式,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎避归,沒想到半個(gè)月后荣月,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梳毙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年哺窄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片账锹。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萌业,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牌废,到底是詐尸還是另有隱情咽白,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布鸟缕,位于F島的核電站晶框,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懂从。R本人自食惡果不足惜授段,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望番甩。 院中可真熱鬧侵贵,春花似錦、人聲如沸缘薛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宴胧。三九已至漱抓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恕齐,已是汗流浹背乞娄。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留显歧,地道東北人仪或。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像士骤,于是被迫代替她去往敵國(guó)和親范删。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 1. 介紹 首先讓我們來(lái)看看TensorFlow拷肌! 但是在我們開始之前瓶逃,我們先來(lái)看看Python API中的Ten...
    JasonJe閱讀 11,721評(píng)論 1 32
  • 網(wǎng)址 下載與安裝 你可以使用我們提供的 Pip, Docker, Virtualenv, Anaconda 或 源...
    九七學(xué)姐閱讀 4,734評(píng)論 3 11
  • 版權(quán)所有束铭,轉(zhuǎn)載請(qǐng)注明出處 其它相關(guān)文章:tensorflow學(xué)習(xí)筆記系列(一):前言tensorflow學(xué)習(xí)筆記系...
    mac在路上閱讀 20,160評(píng)論 2 22
  • 直入正題昔汉。 《荒蠻故事》這部電影由阿根廷導(dǎo)演達(dá)米安·斯茲弗隆編導(dǎo),講述了六個(gè)獨(dú)立的復(fù)仇小故事拴清。影片于2014年8月...
    left的奇思妙想閱讀 591評(píng)論 2 51
  • 朋友過(guò)來(lái)說(shuō)“那天我們一起拉扯的閑話是什么靶病?我還想再聽聽”我說(shuō)全是亂聊的閑話,而且說(shuō)了很多內(nèi)容口予,你要再聽什么內(nèi)容娄周?她...
    朵朵頤閱讀 65評(píng)論 0 2