亚马逊AWS官方博客

玩转 GPU 实例之终结篇 – 深度学习的工具与框架

前言

在开篇的时候我谈到,这个系列的内容来自于我在使用GPU实践中的一点心得。但是围绕GPU相关的技术话题实在是太过于广泛,如果洋洋洒洒的写下去我很担心会错过许多新鲜的话题。于是就让这一篇成为这个系列的终结篇,并且分享一下使用GPU的最重要的领域-深度学习上的框架与工具这个内容吧。

这里我所指的工具是指那些在深度学习应用中发挥了重大作用的几个工具,包括了历久弥新的OpenCV 以及利用Intel CPU加速数学运算的MKL 库(Intel Math Kernel Library )。而深度学习的框架则是那些已经众所周知的深度学习框架,包括了TensorFlowPyTorch 以及Apache MxNet等。

事实上,关于这几个工具与框架的内容可以说是汗牛充栋了。而我想分享的不是如何使用这些工具,而是如何编译、优化这几个工具与框架。针对GPU实例(例如EC2 P3 实例),针对性的优化可以最大限度的发挥出来这个实例的能力,对于模型的训练或者推理的性能会起到显著的提升,如果你有兴趣,就让我们挽起袖子一起来尝试一下。

 

计算机视觉领域的瑞士军刀 – OpenCV

OpenCV(Open Source Computer Vision Library) 是一个开源的计算机视觉和机器学习软件库。OpenCV项目的目标是为了为计算机视觉应用提供一个通用的基础设施。OpenCV采用了BSD许可,这使得开发人员与企业很容易地在自己的产品中使用它。

OpenCV提供了超过2500个优化的算法,其中包括一套全面的经典和最先进的计算机视觉和机器学习算法。这些算法可以用来检测和识别人脸、识别对象,对人的行动的视频分类、跟踪相机移动、跟踪移动物体、提取对象的三维模型,从立体相机产生三维点云图像合成到一起产生一个高分辨率图像的整个场景、从一个图像数据库发现类似的图像、从使用闪光灯拍摄的图像中删除红眼、追踪眼球运动、识别风景并建立标记,将其与增强现实叠加等等。目前OpenCV有超过47,000个用户社区,估计下载量超过1800万。OpenCV是用C++编写的,并且有一个与STL容器无缝配合的模板化接口。怎对开发者OpenCV提供了C++、Python、Java和MATLAB等的接口,并且支持Windows、Linux、Android和Mac OS等平台。

在过去的几年,随着机器学习、深度学习尤其是计算机视觉技术的快速发展,OpenCV的也不断推出了新的特性和版本。其中延续原有接口的3.X系列发展到了3.4.10且表现出来更加的成熟与稳定;而4.X系列则引入了许多新的技术, 例如在版本4.2中增加了引入了对cuDNN的支持使得其DNN模块的性能得到显著提升;对于OpenVINO™ 的支持又使得OpenCV 可以有更好的适应性,例如在RaspBerry Pi 上通过使用Intel® Neural Compute Stick 2计算棒来提升性能。在今年4月份发布的4.3.0 这个版本中,更是一口气增加了对于ONNX、Darknet、MobileNet-SSD 等令人心动的技术的支持。

Intel® Neural Compute Stick 2 (NCS2) 计算棒

不过,在我们使用的Ubuntu 10.04中内置的OpenCV的版本仅仅是3.2。而上述的那许多新的特性难道只能望洋兴叹了吗?那就让我们自己动手编译出来一个吧。我现在使用的OpenCV 就是通过这个脚本编译出来的。脚本的内容如下-

#!/bin/bash
set -e
project_name="opencv/opencv"
VERSION=$(curl --silent "https://github.com/${project_name}/releases/latest" | sed 's#.*tag/\(.*\)\".*#\1#')

sudo apt-get -y install build-essential unzip pkg-config libomp5 libomp-dev
python3.7 -m pip install numpy setuptools wheel -U --user -q

if [ ! -d $HOME/Projects ]; then
  mkdir $HOME/Projects
fi
if [ ! -d $HOME/Downloads ]; then
  mkdir $HOME/Downloads
fi
#Download and unpack opencv source code
cd $HOME/Downloads

if [ ! -f opencv-${VERSION}.zip ]; then
  wget -O opencv-${VERSION}.zip https://github.com/opencv/opencv/archive/${VERSION}.zip -q -o /dev/null
fi
if [ ! -f opencv_contrib-${VERSION}.zip ]; then
  wget -O opencv_contrib-${VERSION}.zip https://github.com/opencv/opencv_contrib/archive/${VERSION}.zip -q -o /dev/null
fi

if [ -d opencv-${VERSION} ]; then
  /bin/rm -rf opencv-${VERSION}
fi
unzip ~/Downloads/opencv-${VERSION}.zip

if [ -d opencv_contrib-${VERSION} ]; then
  /bin/rm -rf opencv_contrib-${VERSION}
fi
unzip ~/Downloads/opencv_contrib-${VERSION}.zip

#install necessary library
sudo apt-get -y install libjpeg-dev libpng-dev libtiff-dev
sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get -y install libxvidcore-dev libx264-dev
sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev libdc1394-22-dev
sudo apt-get -y install libxine2-dev libv4l-dev
sudo apt-get -y install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
sudo apt-get -y install libtbb-dev
#sudo apt-get -y install libgtk-3-dev libgtk2.0-dev qt5-default
sudo apt-get -y install libatlas-base-dev
sudo apt-get -y install software-properties-common
sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"
sudo apt-get -y update
sudo apt-get -y install libjasper1

# Optional dependencies
#sudo apt-get -y install libprotobuf-dev protobuf-compiler
sudo apt-get -y install libgoogle-glog-dev libgflags-dev
sudo apt-get -y install libgphoto2-dev libeigen3-dev libhdf5-dev doxygen

#build source code
cd opencv-${VERSION}
mkdir build
cd build
cmake -G "Unix Makefiles" \
  -D CMAKE_BUILD_TYPE=RELEASE \
  -D CMAKE_INSTALL_PREFIX=/usr/local \
  -D INSTALL_PYTHON_EXAMPLES=OFF \
  -D BUILD_NEW_PYTHON_SUPPORT=ON \
  -D INSTALL_C_EXAMPLES=OFF \
  -D BUILD_TESTS=OFF \
  -D BUILD_PERF_TESTS=OF \
  -D WITH_OPENMP=ON \
  -D WITH_CUDA=ON \
  -D ENABLE_FAST_MATH=ON \
  -D CUDA_FAST_MATH=ON \
  -D WITH_CUDNN=ON \
  -D OPENCV_DNN_CUDA=ON \
  -D WITH_CUBLAS=ON \
  -D WITH_TBB=ON \
  -D WITH_V4L=ON \
  -D WITH_OPENGL=ON \
  -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-${VERSION}/modules \
  -D PYTHON_DEFAULT_EXECUTABLE=/usr/bin/python3.7 \
        -D PYTHON3_EXECUTABLE=/usr/bin/python3.7 \
        -D PYTHON_INCLUDE_DIR=/usr/include/python3.7m \
        -D PYTHON_INCLUDE_DIR2=/home/ubuntu/.local/include/python3.7m \
        -D PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.7m.so \
-D BUILD_OPENCV_PYTHON3=ON \
  -D BUILD_OPENCV_PYTHON2=OFF \
  -D BUILD_OPENCV_JAVA=OFF \
  -D BUILD_EXAMPLES=OFF \
  -D OPENCV_ENABLE_NONFREE=ON \
  -D ENABLE_CXX11=ON \
  -D CUDA_ARCH_BIN="7.0" \
  -D CUDA_ARCH_PTX="" \
  -D WITH_NVCUVID=OFF \
  -D BUILD_OPENCV_CUDACODEC=OFF ..

make -j$(nproc)
sudo make install
sudo ldconfig

这个脚本看起来很长,内容却不复杂。我来给大家解释一下我的思路

VERSION=$(curl --silent "https://github.com/${project_name}/releases/latest" | sed 's#.*tag/\(.*\)\".*#\1#')

这一句使用来取得OpenCV 最新的版本号。这样可以确保我们每一次构建都能够获得最新的版本。

#install necessary library
sudo apt-get -y install libjpeg-dev libpng-dev libtiff-dev
sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get -y install libxvidcore-dev libx264-dev
sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev libdc1394-22-dev
sudo apt-get -y install libxine2-dev libv4l-dev
sudo apt-get -y install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
sudo apt-get -y install libtbb-dev
#sudo apt-get -y install libgtk-3-dev libgtk2.0-dev qt5-default
sudo apt-get -y install libatlas-base-dev
sudo apt-get -y install software-properties-common
sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"
sudo apt-get -y update
sudo apt-get -y install libjasper1

 

这一段安装的是OpenCV所依赖的库。我们可以针对具体的需要酌情删减。

cmake -G "Unix Makefiles" \
  -D CMAKE_BUILD_TYPE=RELEASE \
  -D CMAKE_INSTALL_PREFIX=/usr/local \
  -D INSTALL_PYTHON_EXAMPLES=OFF \
  -D BUILD_NEW_PYTHON_SUPPORT=ON \
  -D INSTALL_C_EXAMPLES=OFF \
  -D BUILD_TESTS=OFF \
  -D BUILD_PERF_TESTS=OF \
  -D WITH_OPENMP=ON \
  -D WITH_CUDA=ON \
  -D ENABLE_FAST_MATH=ON \
  -D CUDA_FAST_MATH=ON \
  -D WITH_CUDNN=ON \
  -D OPENCV_DNN_CUDA=ON \
  -D WITH_CUBLAS=ON \
  -D WITH_TBB=ON \
  -D WITH_V4L=ON \
  -D WITH_OPENGL=ON \
  -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-${VERSION}/modules \
  -D PYTHON_DEFAULT_EXECUTABLE=/usr/bin/python3.7 \
        -D PYTHON3_EXECUTABLE=/usr/bin/python3.7 \
        -D PYTHON_INCLUDE_DIR=/usr/include/python3.7m \
        -D PYTHON_INCLUDE_DIR2=/home/ubuntu/.local/include/python3.7m \
        -D PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.7m.so \
-D BUILD_OPENCV_PYTHON3=ON \
  -D BUILD_OPENCV_PYTHON2=OFF \
  -D BUILD_OPENCV_JAVA=OFF \
  -D BUILD_EXAMPLES=OFF \
  -D OPENCV_ENABLE_NONFREE=ON \
  -D ENABLE_CXX11=ON \
  -D CUDA_ARCH_BIN="7.0" \
  -D CUDA_ARCH_PTX="" \
  -D WITH_NVCUVID=OFF \
  -D BUILD_OPENCV_CUDACODEC=OFF ..

这是编译OpenCV 最重要的步骤,通过cmake 生成编译需要的Makefile。需要注意的是,在这个配置中我启用了对CUDA以及cuDNN 支持。如果我们仅仅需要一个支持CPU的版本可以关闭掉这几个选项。此外,由于重要用于Python3的环境之下,于是在编译中关闭了Python2 与Java的支持。

在一台P3 实例的机器上编译所需的时间不长,我们就得到了在Python 3中使用OpenCV的库- /home/ubuntu/.local/lib/python3.7/site-packages/cv2/python-3.7/cv2.cpython-37m-x86_64-linux-gnu.so 。测试一下,就看到了这个结果

好了,OpenvCV 已经大功告成。

 

CPU 运算的加速器- Intel Math Kernel Library (MKL)

Intel 的MKL是一个为科学、工程和金融应用程序以及机器学习优化数学例程的库。核心数学函数包括BLAS、LAPACK、ScaLAPACK、稀疏求解器、快速傅里叶变换和向量运算等。这个库自2003年出现以来,被广泛的用于利用Intel CPU 指令集中 AVX2、AVX512 等特性来提升运算处理性能。其性能表现可以参考MKL vs OpenBLAS 的一个性能对比。

需要强调一点,Intel 向开发者免费提供了MKL,在软件分发方面需要遵守

Intel Simplified Software License“,而要获得商业支持则需要付费购买。相比较起来,我们可能更熟悉MK-LDNN,这个项目是Intel 的一个开源产品,我们在许多框架当中例如Tensorflow、MXNet中都能看到它。与商业化的Intel MKL 不同,MKL-DNN采用的的是Apache 许可。不过Intel 现在有了一个名为oneAPI 的策略,于是MKL-DNN 被改称oneAPI Deep Neural Network Library (oneDNN)。而Intel MKL 也成为了oneAPI 下的一个重要的组件。与以往的注册、下载、安装不同,MKL 现在提供了一个简单的方法完成在Ubuntu 下的安装。

第一步:添加Intel 的oneAPI repo

#!/bin/bash
set -e

wget -q https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB -O /tmp/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB

if [ -f /tmp/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB ]; then
  sudo apt-key add /tmp/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
  rm /tmp/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
fi

#echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
sudo apt-get update

echo "Done."

第二步、安装Intel MKL

#!/bin/bash
sudo apt update
sudo apt-get install -y intel-mkl-2020.0-088
echo "Done."

第三步、安装其它Intel 的产品。

例如Intel 优化的Python 3 解释器以及TBB 。这里说的TBB指的是Intel 提供的一个C++模板库,用于多核处理器上的并行编程。使用TBB,计算被分解为可以并行运行的任务。该库管理并调度执行这些任务的线程。

#!/bin/bash
sudo apt update
sudo apt-get -y install intelpython3
sudo apt-get -y install intel-tbb-64bit-2020.0-088
echo "Done.

好了,Intel MKL 安装完毕。

 

深度学习框架- TensorFlow、PyTorch 以及MXNet

可以说,TensorFlow、Pytorch 以及Apache MXNet 是最为流行的深度学习框架了。无论是通过官网下载、PIP安装或者Anaconda 等等我们都可以非常容易的完成这几个框架的安装。但是,我们能够想到为了兼顾更为广泛的使用环境的匹配,我们能够得到这几个框架的安装包都只能进行有限度的优化。无非是区分一下使用GPU优化(CUDA、cuDNN)或者CPU优化(MKL-DNN)等等。至于完全匹配我们具体的运行环境的安装包就只能依靠我们自己动手了。

从我的体会来看,动手编译这几个框架固然比较麻烦。但是一来这样可以帮助我们更好的理解框架本身,二来性能提升的效果也是非常令人满意的。

动手编译TensorFlow

动手之前,我们先要解决的一件事就是Bazel 这个构建工具。完全是因为TensorFlow 才让我熟悉了这个工具。考虑到这是Google 的出品,就能够理解TensorFlow放弃Ninja、cmake等成熟工具而选用Bazel 的原因了。Bazel 的运行需要JVM的支持,因此我们需要首先完成JDK 的安装- sudo apt install openjdk-11-jdk。其次,Bazel 的安装有两种方法,第一种是使用Bazel’s apt repository 来进行安装。这种方法最为简单,但却不是我所推荐的方法。原因在于TensorFlow 需要依赖特定的Bazel 版本,于是Binary 安装包就成为了不二的选择。安装脚本如下 –

!/bin/bash
set -e

if ! [ -x "$(command -v curl)" ]; then
  sudo apt install curl -y
fi

#version for tensoflow
#VERSION=$(curl --silent "https://raw.githubusercontent.com/tensorflow/tensorflow/master/configure.py" | awk '/_TF_MAX_BAZEL_VERSION =/{print}' | sed 's/[^0-9|\.]*//g')

#version for tensorflow 2.0
TF_VER="r2.2"
VERSION=$(curl --silent "https://raw.githubusercontent.com/tensorflow/tensorflow/${TF_VER}/configure.py" | awk '/_TF_MAX_BAZEL_VERSION =/{print}' | sed 's/[^0-9|\.]*//g')

#TF_VER="r1.15"
#VERSION=$(curl --silent "https://raw.githubusercontent.com/tensorflow/tensorflow/${TF_VER}/configure.py" | awk '/_TF_MAX_BAZEL_VERSION =/{print}'| sed 's/[^0-9|\.]*//g')

if [ -z $VERSION ]; then
  echo "Can not find the version of bazel."
  exit 1
fi

# Install required packages
sudo apt-get -y install pkg-config zip \
  zlib1g-dev unzip curl

if [ -d "~/.bazel" ]; then
  /bin/rm  -fr $HOME/.bazel $HOME/.bazelrc $HOME/bin/bazel
fi

# Download bazel
if [ ! -d "$HOME/Downloads" ]; then
  mkdir -p $HOME/Downloads
fi
cd $HOME/Downloads

if [[ ! -f "bazel-$VERSION-installer-linux-x86_64.sh" ]]; then
  curl -fSsL -O https://github.com/bazelbuild/bazel/releases/download/$VERSION/bazel-$VERSION-installer-linux-x86_64.sh
fi

# Run the installer
chmod +x bazel-$VERSION-installer-linux-x86_64.sh
./bazel-$VERSION-installer-linux-x86_64.sh --user

# Add $HOME/bin directory to your default paths
echo '#bazel' >> ~/.bashrc
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
source $HOME/.bashrc
$HOME/bin/bazel version
echo "Done."

需要解释的一点,TensorFlow 1.X 与2.X 所使用的Bazel 版本不同,需要我们在脚本中通过注释的方法来选择版本。

TensorFlow 1.5.2

#!/bin/bash
set -e

project_name="tensorflow/tensorflow"
VERSION="1.15.2"

# check wget
if ! [ -x "$(command -v wget)" ]; then
  echo 'Error: wget is not installed.' >&2¬
  sudo apt-get -y install wget
fi

#check directory
if [ ! -d $HOME/Downloads ]; then
  mkdir $HOME/Downloads
fi

cd $HOME/Downloads
if [ ! -f "v${VERSION}.tar.gz" ];then
  wget https://github.com/tensorflow/tensorflow/archive/v${VERSION}.tar.gz -O tensorflow-${VERSION}.tar.gz -q -o /dev/null
fi

if [ ! -d "$HOME/Projects" ]; then
  mkdir -p $HOME/Projects
fi

cd $HOME/Projects
if [ -d tensorflow-${VERSION} ]; then
  /bin/rm -rf tensorflow-${VERSION}
fi
tar xzvf $HOME/Downloads/tensorflow-${VERSION}.tar.gz

if [ ! -d $HOME/.venvs/tensorflow-1_env ]; then
  python3.7 -m venv $HOME/.venvs/tensorflow-1_env
fi

source $HOME/.venvs/tensorflow-1_env/bin/activate
pip install -U pip six numpy wheel setuptools mock future -q
pip install -U keras_applications --no-deps -q
pip install -U keras_preprocessing --no-deps -q

if [ -d tensorflow-${VERSION} ]; then
  cd tensorflow-${VERSION}
else
  echo "Can not find dir of tensorflow-${VERSION}."
  exit 1
fi

if [ -f "tf_config.sh" ]; then
  /bin/rm tf_config.sh
fi

cat <<EOT >> tf_config.sh
#!/bin/bash

source \$HOME/.venvs/tensorflow-1_env/bin/activate

export PYTHONPATH=/home/ubuntu/.venvs/tensorflow-1_env/bin/python3
export PYTHON_BIN_PATH=/home/ubuntu/.venvs/tensorflow-1_env/bin/python3
export PYTHON_LIB_PATH=/home/ubuntu/.venvs/tensorflow-1_env/lib/python3.7/site-packages/

export TF_ENABLE_XLA=1
export TF_NEED_COMPUTECPP=1
export TF_NEED_OPENCL_SYCL=0
export TF_NEED_ROCM=0

export TF_CUDA_COMPUTE_CAPABILITIES="7.0,7.0,7.0,7.0"
export TF_NEED_CUDA=1
export TF_CUDA_VERSION="10.2"
export TF_CUDNN_VERSION="7"
export TF_NCCL_VERSION="2"

export CUDA_TOOLKIT_PATH=/usr/local/cuda
export CUDNN_INSTALL_PATH=/usr
export NCCL_INSTALL_PATH=/usr
export LD_LIBRARY_PATH="/usr/local/cuda-10.2/lib64:/usr/local/cuda-10.2/extras/CUPTI/lib64:"
export GCC_HOST_COMPILER_PATH="/usr/bin/gcc"

export TF_NEED_TENSORRT=0
export TF_NEED_MPI=1
export MPI_INSTALL_PATH=/usr
export MPI_HOME=/usr/local
export CC_OPT_FLAGS='-march=native -Wno-sign-compare'

export TF_CUDA_CLANG=0
export TF_CONFIGURE_IOS=False
export TF_SET_ANDROID_WORKSPACE=False

./configure
EOT
chmod +x tf_config.sh

if [ -f "build_tensorflow.sh" ]; then
  /bin/rm build_tensorflow.sh
fi

cat <<EOT >> build_tensorflow.sh
#!/bin/bash

VERSION="${VERSION}"
prefix=\$(python3 -c "import sys;print(sys.prefix)")
if [[ \${prefix} == *"tensorflow"* ]];then
  echo "You are the venv of tensorflow..."
else
  source \$HOME/.venvs/tensorflow-1_env/bin/activate
fi

export TMP=/tmp
bazel clean --expunge_async
bazel build --copt=-march=native --cxxopt=-march=native \\
        -c opt --copt=-O3 \\
        --config=cuda  \\
        --config=v1 \\
        --config=mkl \\
        --config=nohdfs --config=noignite --config=nokafka \\
        --verbose_failures \\
        //tensorflow/tools/pip_package:build_pip_package
EOT
chmod +x build_tensorflow.sh

if [ -f "build_whl.sh" ]; then
  /bin/rm build_whl.sh
fi

cat <<EOT >> build_whl.sh
#!/bin/bash

VERSION="${VERSION}"
prefix=\$(python3 -c "import sys;print(sys.prefix)")
if [[ \${prefix} == *"tensorflow"* ]];then
  echo "You are the venv of tensorflow..."
else
  source \$HOME/.venvs/tensorflow-1_env/bin/activate
fi

export TMP=/tmp
bazel clean --expunge_async
bazel build --copt=-march=native --cxxopt=-march=native \\
        -c opt --copt=-O3 \\
        --config=cuda  \\
        --config=v1 \\
        --config=mkl \\
        --config=nohdfs --config=noignite --config=nokafka \\
        --verbose_failures \\
        //tensorflow/tools/pip_package:build_pip_package
EOT
chmod +x build_tensorflow.sh

if [ -f "build_whl.sh" ]; then
  /bin/rm build_whl.sh
fi

cat <<EOT >> build_whl.sh
#!/bin/bash

if [ -d /tmp/tensorflow_pkg ]; then
  /bin/rm /tmp/tensorflow_pkg/*
fi

prefix=\$(python3 -c "import sys;print(sys.prefix)")
if [[ \${prefix} == *"tensorflow"* ]];then
        echo "You are the venv of tensorflow..."
else
        source \$HOME/.venvs/tensorflow-1_env/bin/activate
fi

./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
pip3 install /tmp/tensorflow_pkg/*.whl -U
cp /tmp/tensorflow_pkg/*.whl ~/Downloads
cd ..
python3 -c "import tensorflow as tf;print(tf.__version__)"
EOT
chmod +x build_whl.sh

deactivate
echo "Done."

在上面的这个脚本中,我们在~/Project 下克隆了Tensorflow 1.52 的源代码,并且创建了 build_tesnsorflow.sh 与buuild_whel.sh 两个脚本文件。前一个脚本用来编译TensorFlow,后一个脚本用来生成python安装的whl 文件。

编译完成以后我们就得到了Python 包的安装文件tensorflow-1.15.2-cp37-cp37m-linux_x86_64.whl。检查一下可以看到这个结果 –

TensorFlow 2.2

TensorFlow 2.2 的安装与上述的过程比较类似,脚本也大体相同。

#!/bin/bash
set -e

BRANCH="r2.2"
CUDA_VER="10.2"

#check dir
if [ ! -d "$HOME/Projects" ]; then
  mkdir -p $HOME/Projects
fi
cd $HOME/Projects

if [ ! -d $HOME/.venvs/tensorflow-2_env ]; then
  python3.7 -m venv ~/.venvs/tensorflow-2_env
fi

source $HOME/.venvs/tensorflow-2_env/bin/activate
pip install -U pip six numpy wheel setuptools mock future -q
pip install -U keras_applications --no-deps -q
pip install -U keras_preprocessing --no-deps -q

# check git
if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  sudo apt -y install git
fi

if [ -d tensorflow-2.x ]; then
  cd tensorflow-2.x
else
  git clone https://github.com/tensorflow/tensorflow.git tensorflow-2.x
  cd tensorflow-2.x
fi

git clean -f
git checkout ${BRANCH}
git pull

if [ -f "tf_config.sh" ]; then
  /bin/rm tf_config.sh
fi

cat <<EOT >> tf_config.sh
#!/bin/bash

source \$HOME/.venvs/tensorflow-2_env/bin/activate
export PYTHONPATH=/home/ubuntu/.venvs/tensorflow-2_env/bin/python3
export PYTHON_BIN_PATH=/home/ubuntu/.venvs/tensorflow-2_env/bin/python3
export PYTHON_LIB_PATH=/home/ubuntu/.venvs/tensorflow-2_env/lib/python3.7/site-packages/

export TF_ENABLE_XLA=1
export TF_NEED_COMPUTECPP=1
export TF_NEED_OPENCL_SYCL=0
export TF_NEED_ROCM=0

export TF_CUDA_COMPUTE_CAPABILITIES="7.0,7.0,7.0,7.0"
export TF_NEED_CUDA=1
export TF_CUDA_VERSION="${CUDA_VER}"
export TF_CUDNN_VERSION="7"
export TF_NCCL_VERSION="2"

export CUDA_TOOLKIT_PATH=/usr/local/cuda
export CUDNN_INSTALL_PATH=/usr
export NCCL_INSTALL_PATH=/usr
export LD_LIBRARY_PATH="/usr/local/cuda-10.1/lib64:/usr/local/cuda-${CUDA_VER}/extras/CUPTI/lib64:"
export GCC_HOST_COMPILER_PATH="/usr/bin/gcc"

export TF_NEED_TENSORRT=1
export TensorRT=0
export TF_NEED_MPI=1
export MPI_INSTALL_PATH=/usr
export MPI_HOME=/usr/local
export CC_OPT_FLAGS='-march=native -Wno-sign-compare'

export TF_CUDA_CLANG=0
export TF_CONFIGURE_IOS=False
export TF_SET_ANDROID_WORKSPACE=False

./configure
deactivate
EOT
chmod +x tf_config.sh

cat <<EOT >> build_tensorflow.sh
#!/bin/bash

prefix=\$(python3 -c "import sys;print(sys.prefix)")
if [[ \${prefix} == *"tensorflow-2"* ]];then
  echo "You are the venv of tensorflow..."
else
  source \$HOME/.venvs/tensorflow-2_env/bin/activate
fi

export TMP=/tmp
bazel clean --expunge_async
bazel build --copt=-march=native --cxxopt=-march=native \\
        -c opt --copt=-O3 \\
        --config=cuda \\
        --config=nohdfs \\
        --verbose_failures \\
        --config=v2 //tensorflow/tools/pip_package:build_pip_package
EOT
chmod +x build_tensorflow.sh

if [ -f "build_whl.sh" ]; then
  /bin/rm build_whl.sh
fi

cat <<EOT >> build_whl.sh
#!/bin/bash

if [ -d /tmp/tensorflow_pkg ];then
  /bin/rm /tmp/tensorflow_pkga/*
fi

prefix=\$(python3 -c "import sys;print(sys.prefix)")
if [[ \${prefix} == *"tensorflow-2"* ]];then
        echo "You are the venv of tensorflow..."
else
        source \$HOME/.venvs/tensorflow-2_env/bin/activate
fi

./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
pip3 install /tmp/tensorflow_pkg/*.whl -U -q
cp /tmp/tensorflow_pkg/*.whl ~/Downloads
cd ..
python3 -c "import tensorflow as tf;print(tf.__version__)"

EOT
chmod +x build_whl.sh

deactivate
git status
echo "Done."

在这台EC2 P3 8xlarge 的实例上,编译的过程需要大约4100s,也就是一个多小时的时间。提醒一下,不要忘记使用screen 确保网络断线的情况下编译的过程不会中断。

作为编译的结果,我们得到了这个文件tensorflow-2.2.0-cp37-cp37m-linux_x86_64.whl。检查一下版本

动手编译  PyTorch

相比较起来,PyTorch 的编译脚本要简单一些,编译的时间开销也要少一些。编译的脚本如下 –

#!/bin/bash
set -e

#check dir
if [ ! -d "$HOME/Projects" ]; then
  mkdir -p $HOME/Projects
fi
cd $HOME/Projects

# check git
if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  sudo apt -y install git
fi

if [ -d $HOME/.venvs/pytorch_env ];then
  source $HOME/.venvs/pytorch_env/bin/activate
else
  echo "Please setup your pytorch env."
  exit 1
fi

sudo apt install -y libomp-dev libmpfr-dev libgmp-dev libfftw3-dev
pip3 install ninja setuptools wheel numpy pytest -U -q
pip3 install mypy mypy-extensions -U -q

if [ -d pytorch ]; then
  cd pytorch
  git clean -f
else
  # git pull repo
  git clone --recursive https://github.com/pytorch/pytorch
  cd pytorch
fi

# if you are updating an existing checkout
git checkout master
git pull
git submodule sync
git submodule update --init --recursive
pip3 install -r requirements.txt -q

if [ -f  build_pytorch.sh ]; then
  /bin/rm build_pytorch.sh
Fi

cat <<EOT >> build_pytorch.sh
#!/bin/bash

/bin/rm -rf build/*
/bin/rm -rf dist/*

prefix=i\$(python3 -c "import sys;print(sys.prefix)")
if [[ \$prefix == *"pytorch"* ]];then
        echo "You are the venv of pytorch..."
else
        source \$HOME/.venvs/pytorch_env/bin/activate
fi

python3 setup.py clean
USE_CUDA=1 \\
USE_CUDNN=1 \\
CUDNN_LIB_DIR=/usr/lib/x86_64-linux-gnu \\
CUDNN_INCLUDE_DIR=/usr/include \\
BUILD_TEST=0 \\
USE_MKLDNN=1 \\
USE_NNPACK=1 \\
USE_DISTRIBUTED=1 \\
USE_SYSTEM_NCCL=1 \\
USE_NCCL=1 \\
USE_OPENCV=1 \\
NCCL_ROOT=/usr \\
NCCL_INCLUDE_DIR=/usr/include \\
NCCL_LIB_DIR=/usr/lib/x86_64-linux-gnu \\
USE_TENSORRT=0 \\
BUILD_BINARY=1 \\
BUILD_TORCH=ON \\
python3 setup.py bdist_wheel

#install torch
if [ -f dist/*.whl ]; then
  pip3 install -U dist/*.whl -q
  cp dist/*.whl ~/Downloads
fi
EOT

chmod +x build_pytorch.sh
deactivate
echo "Done."

编译成功以后我们就得到了一个Python 安装包- torch-1.6.0a0+30e7055-cp37-cp37m-linux_x86_64.whl。验证一下结果,

此外,我还可以从其它的几个方面针对PyTorch 进行优化,例如torchversion 以及pillow。具体说来就是针对这两个工具在编译中引入SIMD 以及AVX指令的优化,使之性能更加出色。这里说的SIMD指的是单指令流多数据流(Single Instruction Multiple Data)是一种采用一个控制器来控制多个处理器,同时对一组数据(又称“数据向量”)中的每一个分别执行相同的操作从而实现空间上的并行性的技术。

pillow-simd 的安装脚本如下 –

#!/bin/bash

sudo apt-get install -y \
    libjpeg-turbo8-dev \
    zlib1g-dev \
    libtiff5-dev \
    liblcms2-dev \
    libfreetype6-dev \
    libwebp-dev \
    libharfbuzz-dev \
    libfribidi-dev \
    libopenjp2-7-dev \
    libraqm0

prefix=$(python3 -c "import sys;print(sys.prefix)")
if [[ $prefix == *"pytorch"* ]];then
        echo "You are the venv of pytorch..."
else
        source $HOME/.venvs/pytorch_env/bin/activate
fi
pip3 uninstall -y pillow-simd || true


CC="cc -mavx2 -msse4" pip3 install --no-cache-dir -U -I --force-reinstall pillow-simd \
    --global-option="build_ext" \
    --global-option="--enable-zlib" \
    --global-option="--enable-jpeg" \
    --global-option="--enable-tiff" \
    --global-option="--enable-freetype" \
    --global-option="--enable-lcms" \
    --global-option="--enable-webp" \
    --global-option="--enable-webpmux" \
    --global-option="--enable-jpeg2000"
deactivate

prefix=$(python3 -c "import sys;print(sys.prefix)")
if [[ $prefix == *"tensorflow"* ]];then
        echo "You are the venv of tensorflow..."
else
        source $HOME/.venvs/tensorflow-2_env/bin/activate
fi
pip3 uninstall -y pillow-simd || true

CC="cc -mavx2 -msse4" pip3 install --no-cache-dir -U -I --force-reinstall pillow-simd \
    --global-option="build_ext" \
    --global-option="--enable-zlib" \
    --global-option="--enable-jpeg" \
    --global-option="--enable-tiff" \
    --global-option="--enable-freetype" \
    --global-option="--enable-lcms" \
    --global-option="--enable-webp" \
    --global-option="--enable-webpmux" \
    --global-option="--enable-jpeg2000"
deactivate

source $HOME/.venvs/tensorflow-1_env/bin/activate
pip3 uninstall -y pillow-simd || true
CC="cc -mavx2 -msse4" pip3 install --no-cache-dir -U -I --force-reinstall pillow-simd \
    --global-option="build_ext" \
    --global-option="--enable-zlib" \
    --global-option="--enable-jpeg" \
    --global-option="--enable-tiff" \
    --global-option="--enable-freetype" \
    --global-option="--enable-lcms" \
    --global-option="--enable-webp" \
    --global-option="--enable-webpmux" \
    --global-option="--enable-jpeg2000"
deactivate

echo "Done."

 

而torchversion 这个PyTorch 的最佳拍档,其安装脚本如下-

#!/bin/bash
set -e

#check dir
if [ ! -d "$HOME/Projects" ]; then
  mkdir -p $HOME/Projects
fi
cd $HOME/Projects

# check git
if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  sudo apt-get -y install git
fi

sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev

prefix=$(python3 -c "import sys;print(sys.prefix)")
if [[ $prefix == *"pytorch"* ]];then
  echo "You are the venv of pytorch..."
else
  source $HOME/.venvs/pytorch_env/bin/activate
fi

#uninstall torchvision
pip3 uninstall -y vision || true

# git pull repo
if [ ! -d vision ];then
  git clone https://github.com/pytorch/vision.git
fi
cd vision
git checkout master
git pull

#if [ -d build ];then
/bin/rm -rf build/*
/bin/rm -rf dist/*
#fi
python3 setup.py clean

FORCE_CUDA=1 python3 setup.py bdist_wheel
if [ -f dist/*.whl ]; then
  pip3 install -U dist/*.whl -q
  cp dist/*.whl ~/Downloads
fi
deactivate

echo "Done."

这里一个主要的优化手段就是使得torchvision 使用GPU 来进行加速。

动手编译 Apache MXNet

MXNet 这个项目虽非声名显赫,但其巨大的进步也是有目共睹,最新版本已经发展到了1.6.0。对我来说,我更期待其2.0的版本。在MXNet 2.0 Roadmap 中我们看到了许多令人激动的特性,希望这个版本正式发布的日子早点到了。

编译MXNet 不是件麻烦的事情,在官方文档中有详尽的描述。我的编译脚本是这样的-

#!/bin/bash
set -e

project_name="apache/incubator-mxnet"
VERSION=$(curl --silent "https://github.com/${project_name}/releases/latest" | sed 's#.*tag/v\(.*\)\".*#\1#')

sudo apt-get update
sudo apt-get install -y ninja-build ccache libopenblas-base libopenblas-dev libjemalloc-dev liblapack-dev graphviz

if ! [ -x "$(command -v cmake)" ]; then
  echo "Please install cmake."
  exit 1
fi

#check dir
if [ ! -d "$HOME/Projects" ]; then
  mkdir -p $HOME/Projects
fi
cd $HOME/Projects

if [ ! -d $HOME/.venvs/mxnet_env ]; then
  python3.7 -m venv ~/.venvs/mxnet_env
fi

source $HOME/.venvs/mxnet_env/bin/activate
pip3 install -U pip numpy wheel setuptools graphviz -q

# check git
if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  sudo apt -y install git
fi

if [ -d mxnet ]; then
  cd mxnet
  git pull
  git submodule sync
  git submodule update --init --recursive
else
  git clone --recursive https://github.com/apache/incubator-mxnet.git mxnet
  cd mxnet
fi

git clean -f
#git checkout tags/${VERSION}

/bin/rm -rf build
fi
cd build
cmake -GNinja \
    -DUSE_CUDA=ON \
    -DUSE_CUDNN=ON \
    -DUSE_NCCL=ON \
    -DUSE_LAPACK=1 \
    -DUSE_OPENMP=1 \
    -DUSE_OPENCV=1 \
    -DUSE_DIST_KVSTORE=0 \
    -DUSE_MKL_IF_AVAILABLE=ON \
    -DCMAKE_BUILD_TYPE=Release \
..
ninja -v

if [ -f libmxnet.so ];then
  cp libmxnet.so $HOME/.venvs/mxnet_env/lib/python3.7/site-packages/mxnet/
else
  echo "Build libmxnet failed."
  exit 1
fi

# install python package
cd ../python
python3 setup.py bdist_wheel

if [ -f dist/*.whl ]; then
  pip3 install -U dist/*.whl -q
  cp dist/*.whl ~/Downloads
  sudo ldconfig
fi

echo "Done."

好了,到这里这个系列终于可以告一段落了。其实,在我的构思中其实还有一些内容来不及加入进来,例如Horovod、JupyterLab、TensorFlow Lite 以及TVM 等等。如果有机会,我还是希望能够与各位分享这些内容。随着时间的推移,更多有意思的内容也会不断的涌现,就像是我现在正在玩 的YOLOv4 一样。希望这些内容能够对各位有所稗益,也预祝各位在技术的海洋中自得其乐。

 

本篇作者


费良宏

费良宏,AWS Principal Developer Advocate。在过去的20多年一直从事软件架构、程序开发以及技术推广等领域的工作。他经常在各类技术会议上发表演讲进行分享,他还是多个技术社区的热心参与者。他擅长Web领域应用、移动应用以及机器学习等的开发,也从事过多个大型软件项目的设计、开发与项目管理。目前他专注与云计算以及互联网等技术领域,致力于帮助中国的 开发者构建基于云计算的新一代的互联网应用。