2017년 10월 16일 월요일

Minsky 서버에서 Tensorflow r1.3을 source로부터 build하기


2017년 10월 현재 최신 PowerAI 버전은 4.0으로서, 이 속에 포함된 tensorflow는 버전 r1.1입니다.

root@ubuntu:/tmp/kkk# dpkg -l | grep mldl
ii  mldl-repo-local                        4.0.0                                      ppc64el      IBM repository for Deep Learning tools for POWER linux
ii  power-mldl                             4.0.0                                      ppc64el      Meta-package for Deep Learning frameworks for POWER


root@ubuntu:/tmp/kkk# dpkg -l | grep tensor
ii  ddl-tensorflow                         0.9.0-4ibm1                                ppc64el      TensorFlow operator for IBM PowerAI Distributed Deep Learning
ii  tensorflow                             1.1.0-4ibm1                                ppc64el      Open source software library for numerical computation

현재 github에 올라온 tensorflow 최신 버전은 r1.4이며, 몇몇 최신 버전을 사용하시는 고객께서는 r1.3을 테스트하시고자 합니다.  PowerAI는 대략 3개월 단위로 최신 ML/DL framework 버전을 갱신하니까, 다음 버전의 PowerAI는 아마도 11월 경에 나올 가능성이 많습니다.  그렇다면 tensorflow나 기타 framework의 최신 버전을 쓰려면 그때까지 참고 기다려야 할까요 ?

일단 기다려주십사 하는 것이 IBM의 공식 입장입니다만, 사실 꼭 그러실 필요는 없습니다.  오픈 소스니까요.  그냥 tensorflow r1.3을 직접 소스로부터 빌드하셔서 사용하실 수도 있습니다.  여기서는 그 방법을 살펴보시고, r1.3이 포함된 site-packages.tar도 올려놓겠습니다.

먼저, 여기서 제가 빌드를 위해 사용한 환경은 Ubuntu 16.04.2 LTS ppc64le입니다.  CUDA는 8.0.61, libcudnn은 6.0.21, 그리고 gcc는 5.4 입니다.

u0017649@sys-89490:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.2 LTS
Release:        16.04
Codename:       xenial


u0017649@sys-89490:~$ dpkg -l | grep cuda
ii  cuda                                                     8.0.61-1                                   ppc64el      CUDA meta-package
ii  cuda-8-0                                                 8.0.61-1                                   ppc64el      CUDA 8.0 meta-package
...
ii  libcudnn6                                                6.0.21-1+cuda8.0                           ppc64el      cuDNN runtime libraries
ii  libcudnn6-dev                                            6.0.21-1+cuda8.0                           ppc64el      cuDNN development libraries and headers

u0017649@sys-89490:~$ /usr/bin/gcc --version
gcc (Ubuntu/IBM 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

여기서는 아래의 tensorflow 공식 홈페이지에 나온 source로부터의 빌드 방법을 거의 그대로 따라가겠습니다. 

https://www.tensorflow.org/install/install_sources#ConfigureInstallation

Tensorflow 홈페이지에서는 아래의 libcupti-dev 외에 python 관련 패키지를 잔뜩 설치해야 하라고 나오는데, 실제로는 그냥 anaconda만 설치하고 진행해도 괜찮은 것 같습니다.

u0017649@sys-89490:~$ sudo apt-get install libcupti-dev

아래는 ananconda3 설치 절차입니다.

u0017649@sys-89490:~$ wget wget https://repo.continuum.io/archive/Anaconda3-4.4.0.1-Linux-ppc64le.sh

u0017649@sys-89490:~$ chmod u+x Anaconda*

u0017649@sys-89490:~$ sudo ./Anaconda3-4.4.0.1-Linux-ppc64le.sh
...
[/home/u0017649/anaconda3] >>> /usr/local/anaconda3

u0017649@sys-89490:~$ export PATH="/usr/local/anaconda3/bin:$PATH"

여기서는 주로 u0017649 userid를 사용하므로, 그 userid로 anaconda root directory(/usr/local/anaconda3)를 자유롭게 쓸 수 있도록 그 directory의 ownership을 바꿔줍니다.

u0017649@sys-89490:~$ sudo chown -R u0017649:u0017649 /usr/local/anaconda3

이제 conda 명령을 이용해서 bazel과 numpy를 설치합니다.

u0017649@sys-89490:~$ conda install bazel numpy
Fetching package metadata .........
Solving package specifications: .

Package plan for installation in environment /usr/local/anaconda3:

The following NEW packages will be INSTALLED:

    bazel:    0.4.5-0

The following packages will be UPDATED:

    anaconda: 4.4.0-np112py36_0 --> 4.4.0-np112py36_1
    conda:    4.3.21-py36_0     --> 4.3.27-py36_0

Proceed ([y]/n)? y

bazel-0.4.5-0. 100% |####################################################| Time: 0:00:09  14.58 MB/s
conda-4.3.27-p 100% |####################################################| Time: 0:00:00  10.31 MB/s
anaconda-4.4.0 100% |####################################################| Time: 0:00:00   6.57 MB/s

u0017649@sys-89490:~$ which bazel
/usr/local/anaconda3/bin/bazel

이제 tensorflow의 source를 git clone 명령으로 download 받습니다.

u0017649@sys-89490:~$ git clone --recursive https://github.com/tensorflow/tensorflow.git
Cloning into 'tensorflow'...
remote: Counting objects: 246390, done.
remote: Total 246390 (delta 0), reused 0 (delta 0), pack-reused 246390
Receiving objects: 100% (246390/246390), 125.14 MiB | 6.34 MiB/s, done.
Resolving deltas: 100% (192026/192026), done.
Checking connectivity... done.

우리가 빌드하려는 것은 r1.3으로 그 branch로 switch합니다.

u0017649@sys-89490:~$ cd tensorflow

u0017649@sys-89490:~/tensorflow$ git checkout r1.3
Checking out files: 100% (3934/3934), done.
Branch r1.3 set up to track remote branch r1.3 from origin.
Switched to a new branch 'r1.3'

이제 build 준비가 끝났습니다.  Build 절차는 기본적으로, configure --> bazel로 pip package build --> wheel file build  --> pip 명령으로 wheel file 설치입니다.

Configure하실 때 대부분은 default를 선택하시되, 몇몇 부분에서는 별도로 지정하셔야 합니다.  ppc64le 특성에 맞춰야 하는 부분은 딱 한군데, cuDNN 6 library가 위치한 directory를 지정하는 부분 뿐입니다.

u0017649@sys-89490:~/tensorflow$ ./configure
Extracting Bazel installation...
..................
You have bazel 0.4.5- installed.
Please specify the location of python. [Default is /usr/local/anaconda3/bin/python]:
Found possible Python library paths:
  /usr/local/anaconda3/lib/python3.6/site-packages
Please input the desired Python library path to use.  Default is [/usr/local/anaconda3/lib/python3.6/site-packages]
Do you wish to build TensorFlow with MKL support? [y/N] n
...
Do you wish to build TensorFlow with CUDA support? [y/N] y
...
Please specify which gcc should be used by nvcc as the host compiler. [Default is /opt/at10.0/bin/gcc]: /usr/bin/gcc
...
Please specify the location where cuDNN 6 library is installed. Refer to README.md for more details. [Default is /usr/local/cuda]: /usr/lib/powerpc64le-linux-gnu
...
You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus.
Please note that each additional compute capability significantly increases your build time and binary size.
[Default is: "3.5,5.2"]: "3.7,6.0"       # 3.7은 K80, 6.0은 P100을 위한 compute capability입니다.
...

Configure가 끝나면 bazel로 TensorFlow with GPU support를 위한 pip package를 build합니다.  이 과정 중에, 아래 예시한 것과 같이 인터넷으로부터 일부 tar.gz 파일들을 download 받는 부분이 관측됩니다.  즉, 이 과정을 위해서는 인터넷 연결이 필요합니다.

u0017649@sys-89490:~/tensorflow$ bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package
...
INFO: Downloading http://mirror.bazel.build/github.com/google/boringssl/archive/bbcaa15b0647816b9a\
1a9b9e0d209cd6712f0105.tar.gz: 3,244,032 bytes
...
INFO: Downloading http://mirror.bazel.build/github.com/google/boringssl/archive/bbcaa15b0647816b9a\
1a9b9e0d209cd6712f0105.tar.gz: 4,032,059 bytes
...
INFO: From Compiling tensorflow/python/pywrap_tensorflow_internal.cc:
bazel-out/local_linux-py3-opt/bin/tensorflow/python/pywrap_tensorflow_internal.cc: In function 'PyObject* _wrap_PyRecordReader_New(PyObject*, PyObject*)':
bazel-out/local_linux-py3-opt/bin/tensorflow/python/pywrap_tensorflow_internal.cc:5173:138: warning: 'arg2' may be used uninitialized in this function [-Wmaybe-uninitialized]
     result = (tensorflow::io::PyRecordReader *)tensorflow::io::PyRecordReader::New((string const &)*arg1,arg2,(string const &)*arg3,arg4);
                                                                                                                                          ^
At global scope:
cc1plus: warning: unrecognized command line option '-Wno-self-assign'
Target //tensorflow/tools/pip_package:build_pip_package up-to-date:
  bazel-bin/tensorflow/tools/pip_package/build_pip_package
INFO: Elapsed time: 7106.091s, Critical Path: 4427.34s

이런저런 warning message만 나올 뿐, 잘 끝났습니다.  저는 이 과정을 1-core짜리 cloud 환경에서 돌렸는데, 이렇게 거의 2시간 가까이 걸렸습니다.

이제 여기서 build된 binary를 이용하여 .whl file을 build합니다.  생성될 .whl 파일은 /tmp/tensorflow_pkg directory에 들어가도록 지정합니다.

u0017649@sys-89490:~/tensorflow$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
Fri Oct 13 03:40:03 EDT 2017 : === Using tmpdir: /tmp/tmp.UreS6KLhqt
~/tensorflow/bazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles ~/tensorflow
~/tensorflow
/tmp/tmp.UreS6KLhqt ~/tensorflow
Fri Oct 13 03:40:23 EDT 2017 : === Building wheel
warning: no files found matching '*.dll' under directory '*'
warning: no files found matching '*.lib' under directory '*'
warning: no files found matching '*.h' under directory 'tensorflow/include/tensorflow'
warning: no files found matching '*' under directory 'tensorflow/include/Eigen'
warning: no files found matching '*' under directory 'tensorflow/include/external'
warning: no files found matching '*.h' under directory 'tensorflow/include/google'
warning: no files found matching '*' under directory 'tensorflow/include/third_party'
warning: no files found matching '*' under directory 'tensorflow/include/unsupported'
~/tensorflow
Fri Oct 13 03:44:36 EDT 2017 : === Output wheel file is in: /tmp/tensorflow_pkg


u0017649@sys-89490:~$ ls /tmp/tensorflow_pkg
tensorflow-1.3.1-cp36-cp36m-linux_ppc64le.whl

잘 끝났습니다.  이제 이 wheel 파일을 pip 명령으로 설치합니다.  단, 그 전에 PYTHONPATH 환경변수를 확실히 지정할 것을 권고합니다.  여기서도 tensorflow 설치에 필요한 tensorboard나 protobuf 등을 인터넷으로부터 자동으로 download 받는 것 같습니다.

u0017649@sys-89490:~$ export PYTHONPATH=/usr/local/anaconda3/lib/python3.6/site-packages

u0017649@sys-89490:~$ pip install /tmp/tensorflow_pkg/tensorflow-1.3.1-cp36-cp36m-linux_ppc64le.whl
Processing /tmp/tensorflow_pkg/tensorflow-1.3.1-cp36-cp36m-linux_ppc64le.whl
Requirement already satisfied: six>=1.10.0 in /usr/local/anaconda3/lib/python3.6/site-packages (from tensorflow==1.3.1)
Collecting tensorflow-tensorboard<0.2.0,>=0.1.0 (from tensorflow==1.3.1)
  Retrying (Retry(total=4, connect=None, read=None, redirect=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='pypi.python.org', port=443): Read timed out. (read timeout=15)",)': /simple/tensorflow-tensorboard/
  Downloading tensorflow_tensorboard-0.1.8-py3-none-any.whl (1.6MB)
    100% |????????????????????????????????| 1.6MB 772kB/s
Requirement already satisfied: wheel>=0.26 in /usr/local/anaconda3/lib/python3.6/site-packages (from tensorflow==1.3.1)
Requirement already satisfied: numpy>=1.11.0 in /usr/local/anaconda3/lib/python3.6/site-packages (from tensorflow==1.3.1)
Collecting protobuf>=3.3.0 (from tensorflow==1.3.1)
  Downloading protobuf-3.4.0-py2.py3-none-any.whl (375kB)
    100% |????????????????????????????????| 378kB 2.5MB/s
Requirement already satisfied: werkzeug>=0.11.10 in /usr/local/anaconda3/lib/python3.6/site-packages (from tensorflow-tensorboard<0.2.0,>=0.1.0->tensorflow==1.3.1)
...
Successfully installed html5lib-0.9999999 markdown-2.6.9 protobuf-3.4.0 tensorflow-1.3.1 tensorflow-tensorboard-0.1.8

자, 이제 설치가 끝났습니다.  conda list 명령으로 tensorflow의 설치를 확인합니다.

u0017649@sys-89490:~$ conda list | grep tensor
tensorflow                1.3.1                     <pip>
tensorflow-tensorboard    0.1.8                     <pip>

이제 tensorflow가 설치된 PYTHONPATH, 즉 /usr/local/anaconda3/lib/python3.6/site-packages를 통째로 tar로 말아서 K80 GPU가 설치된 제2의 서버에 가져가겠습니다.  물론 그 제2의 서버에는 미리 anaconda3가 설치되어 있어야 합니다.

u0017649@sys-89498:/usr/local/anaconda3/lib/python3.6 $ tar -zcf site-packages.tgz site-packages

u0017649@sys-89498:~$ scp /usr/local/anaconda3/lib/python3.6/site-packages.tgz firestone:~/
site-packages.tgz                             100%  313MB  19.6MB/s   00:16

이제 위에서 firestone이라고 되어 있는 그 서버로 이동해서 작업합니다.  참고로, 이 firestone이라는 서버는 Ubuntu 16.04.3으로서 빌드 서버의 16.04.2보다 약간 버전이 높습니다만, 별 문제 없이 잘 수행되더군요.

먼저 anaconda3가 설치되어 있는지 확인합니다.

root@ubuntu:~# which conda
/root/anaconda3/bin/conda

여기서는 root user로 /root/anaconda3 위치에 anaconda를 설치해놓았네요.  뭐 별로 좋은 위치는 아닙니다만, 여기서는 이걸 그대로 써보겠습니다.  여기서의 PYTHONPATH, 즉, /root/anaconda3/lib/python3.6/site-packages에 저 위의 서버에서 가져온 site-packages.tgz를 그대로 풀어놓겠습니다.  기존의 site-packages 속에 들어있는 package들은 overwrite 됩니다만, 만약 새로 풀리는 site-packages 밑에 없는 패키지들이 있었다면 그것들은 그대로 보존되니까, 그냥 풀어놓으셔도 됩니다.

root@ubuntu:~# cd /root/anaconda3/lib/python3.6

root@ubuntu:~/anaconda3/lib/python3.6# tar -zxf ~/site-packages.tgz

이제 준비 완료입니다.  먼저 conda list로 tensorflow가 설치되어 있는 것으로 나오는지 확인해봅니다.

root@ubuntu:~/anaconda3/lib/python3.6# conda list | grep tensorflow
tensorflow                1.3.1                     <pip>
tensorflow-tensorboard    0.1.8                     <pip>

예, 잘 보이네요.  이제 PYTHONPATH를 다시 한번 확실히 선언하고, 시험 삼아 cifar10을 수행해보겠습니다.

root@ubuntu:~# export PYTHONPATH=/root/anaconda3/lib/python3.6/site-packages

root@ubuntu:~# git clone https://github.com/tensorflow/models.git
Cloning into 'models'...
remote: Counting objects: 7644, done.
remote: Total 7644 (delta 0), reused 0 (delta 0), pack-reused 7644
Receiving objects: 100% (7644/7644), 157.90 MiB | 19.09 MiB/s, done.
Resolving deltas: 100% (4139/4139), done.
Checking connectivity... done.

root@ubuntu:~# cd models/tutorials/image/cifar10

root@ubuntu:~/models/tutorials/image/cifar10# ls
BUILD             cifar10_input_test.py       cifar10_train.py
cifar10_eval.py   cifar10_multi_gpu_train.py  __init__.py
cifar10_input.py  cifar10.py                  README.md

root@ubuntu:~/models/tutorials/image/cifar10# which python
/root/anaconda3/bin/python

Download 받은 example script 중 cifar10_train.py을 수행하면 training dataset의 download부터 training까지 일사천리로 수행됩니다.

root@ubuntu:~/models/tutorials/image/cifar10# python cifar10_train.py
>> Downloading cifar-10-binary.tar.gz 100.0%
Successfully downloaded cifar-10-binary.tar.gz 170052171 bytes.
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:127: RuntimeWarning: overflow encountered in add
  a = a + a
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:129: RuntimeWarning: invalid value encountered in subtract
  temp1 = temp - a
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:138: RuntimeWarning: invalid value encountered in subtract
  itemp = int_conv(temp-a)
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:162: RuntimeWarning: overflow encountered in add
  a = a + a
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:164: RuntimeWarning: invalid value encountered in subtract
  temp1 = temp - a
/root/anaconda3/lib/python3.6/site-packages/numpy/core/machar.py:171: RuntimeWarning: invalid value encountered in subtract
  if any(temp-a != zero):
Filling queue with 20000 CIFAR images before starting to train. This will take a few minutes.
...
2017-10-13 22:18:12.974909: I tensorflow/core/common_runtime/gpu/gpu_device.cc:986] 2:   N N Y Y
2017-10-13 22:18:12.974915: I tensorflow/core/common_runtime/gpu/gpu_device.cc:986] 3:   N N Y Y
2017-10-13 22:18:12.974933: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla K80, pci bus id: 0000:03:00.0)
2017-10-13 22:18:12.974942: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:1) -> (device: 1, name: Tesla K80, pci bus id: 0000:04:00.0)
2017-10-13 22:18:12.974952: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:2) -> (device: 2, name: Tesla K80, pci bus id: 0020:03:00.0)
2017-10-13 22:18:12.974960: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:3) -> (device: 3, name: Tesla K80, pci bus id: 0020:04:00.0)
2017-10-13 22:18:22.502001: step 0, loss = 4.67 (52.2 examples/sec; 2.451 sec/batch)
2017-10-13 22:18:22.917980: step 10, loss = 4.62 (3076.9 examples/sec; 0.042 sec/batch)
2017-10-13 22:18:23.240470: step 20, loss = 4.43 (3969.1 examples/sec; 0.032 sec/batch)
2017-10-13 22:18:23.538250: step 30, loss = 4.41 (4298.5 examples/sec; 0.030 sec/batch)
2017-10-13 22:18:23.837076: step 40, loss = 4.29 (4283.4 examples/sec; 0.030 sec/batch)
2017-10-13 22:18:24.134108: step 50, loss = 4.33 (4309.3 examples/sec; 0.030 sec/batch)
2017-10-13 22:18:24.426799: step 60, loss = 4.30 (4373.2 examples/sec; 0.029 sec/batch)
...
2017-10-14 07:09:50.128824: step 999920, loss = 0.10 (4352.2 examples/sec; 0.029 sec/batch)
2017-10-14 07:09:50.428406: step 999930, loss = 0.11 (4272.7 examples/sec; 0.030 sec/batch)
2017-10-14 07:09:50.722993: step 999940, loss = 0.14 (4345.0 examples/sec; 0.029 sec/batch)
2017-10-14 07:09:51.019963: step 999950, loss = 0.11 (4310.2 examples/sec; 0.030 sec/batch)
2017-10-14 07:09:51.317448: step 999960, loss = 0.13 (4302.7 examples/sec; 0.030 sec/batch)
2017-10-14 07:09:51.617180: step 999970, loss = 0.11 (4270.7 examples/sec; 0.030 sec/batch)
2017-10-14 07:09:51.914633: step 999980, loss = 0.14 (4303.0 examples/sec; 0.030 sec/batch)
2017-10-14 07:09:52.213837: step 999990, loss = 0.13 (4278.1 examples/sec; 0.030 sec/batch)


완벽하게 잘 수행되었습니다.

아래의 제 구글 드라이브에 site-packages.tgz를 올려두었습니다.  사이즈는 330MB 정도인 이 파일은 ppc64le 아키텍처의 Ubuntu 16.04, CUDA 8.0, libcuDNN 6.0 환경의 K80 또는 P100을 활용하실 수 있도록 빌드된 것입니다.  필요하시면 마음대로 가져가 쓰셔도 됩니다.   제가 책임은 질 수 없는 패키지라는 것은 양해 바랍니다.

https://drive.google.com/open?id=0B-F0jEb44gqUZGdYb3A2QUNaenM 

2017년 10월 10일 화요일

Minsky 위에서의 PyTorch 설치, 그리고 MNIST 수행


IBM에서 제공되는 주요 오픈소스 기반 Deep Learning framework toolkit인 PowerAI에는 아직 PyTorch가 포함되어 있지 않습니다.  그러나 요즘 python의 인기를 타고 PyTorch를 사용하는 사례가 점점 늘고 있습니다.

ppc64le 아키텍처 기반의 IBM Minsky 서버에서는 그렇다면 아직 PyTorch를 사용 못하는 것인가 ?  아닙니다.  오픈소스 좋은 것이 무엇이겠습니까 ?  그냥 source에서 빌드하셔서 사용하시면 됩니다.   여기서는 그 과정을 한번 보겠습니다.   제가 가난하여 Minsky가 없는 관계로, Kolon Benit에서 잠깐 빌려주신 Firestone 서버 (POWER8 + K80)에서 빌드하고 테스트했습니다.   여기서는 Ubuntu 16.04.03에 CUDA 8.0.61을 썼습니다.

root@ubuntu:/data/examples/mnist# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial

root@ubuntu:/data/examples/mnist# dpkg -l | grep cuda
ii  cuda                                    8.0.61-1                                   ppc64el      CUDA meta-package
ii  cuda-8-0                                8.0.61-1                                   ppc64el      CUDA 8.0 meta-package


먼저 아래 URL에 따라 anaconda를 설치합니다.

http://hwengineer.blogspot.kr/2017/09/minsky-anaconda-installer-full-package.html

다음으로 github에서 source download 합니다.

root@ubuntu:/data# git clone https://github.com/pytorch/pytorch.git
Cloning into 'pytorch'...
remote: Counting objects: 40225, done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 40225 (delta 33), reused 30 (delta 24), pack-reused 40162
Receiving objects: 100% (40225/40225), 15.52 MiB | 6.41 MiB/s, done.
Resolving deltas: 100% (30571/30571), done.
Checking connectivity... done.

root@ubuntu:/data# cd pytorch

Anaconda로부터 conda가 제대로 설치되었는지 확인하고, 그에 따라 CMAKE_PREFIX_PATH를 설정합니다.

root@ubuntu:/data/pytorch# which conda
/opt/anaconda2/bin/conda

root@ubuntu:/data/pytorch# export CMAKE_PREFIX_PATH=/opt/anaconda2

이어서 numpy와 pyyaml 등 PyTorch에 필요한 python package들을 conda 명령으로 설치합니다.  이때 원래의 build instruction (https://github.com/pytorch/pytorch#from-source)에서는 intel에만 있는 mkl도 conda로 설치하라고 나옵니다만, ppc64le에서는 그 대신 openblas를 설치하시면 됩니다.

root@ubuntu:/data/pytorch# conda install numpy pyyaml setuptools cmake cffi openblas
...
Package plan for installation in environment /opt/anaconda2:

The following NEW packages will be INSTALLED:

    bzip2:        1.0.6-3
    certifi:      2016.2.28-py27_0
    cmake:        3.6.3-0

The following packages will be UPDATED:

    anaconda:     4.4.0-np112py27_0  --> custom-py27_0
    astropy:      1.3.2-np112py27_0  --> 2.0.1-np113py27_1
    bottleneck:   1.2.1-np112py27_0  --> 1.2.1-np113py27_1
    conda:        4.3.21-py27_0      --> 4.3.27-py27_0
    h5py:         2.7.0-np112py27_0  --> 2.7.0-np113py27_1
    matplotlib:   2.0.2-np112py27_0  --> 2.0.2-np113py27_0
    numexpr:      2.6.2-np112py27_0  --> 2.6.2-np113py27_1
    numpy:        1.12.1-py27_0      --> 1.13.1-py27_1
    pandas:       0.20.1-np112py27_0 --> 0.20.3-py27_1
    pytables:     3.2.2-np112py27_4  --> 3.4.2-np113py27_0
    pywavelets:   0.5.2-np112py27_0  --> 0.5.2-np113py27_1
    scikit-image: 0.13.0-np112py27_0 --> 0.13.0-np113py27_0
    scikit-learn: 0.18.1-np112py27_1 --> 0.19.0-np113py27_1
    scipy:        0.19.0-np112py27_0 --> 0.19.1-np113py27_1
    setuptools:   27.2.0-py27_0      --> 36.4.0-py27_1
    statsmodels:  0.8.0-np112py27_0  --> 0.8.0-np113py27_1

Proceed ([y]/n)? y

bzip2-1.0.6-3. 100% |################################| Time: 0:00:00  10.23 MB/s
anaconda-custo 100% |################################| Time: 0:00:00  15.66 MB/s
certifi-2016.2 100% |################################| Time: 0:00:01 147.40 kB/s
cmake-3.6.3-0. 100% |################################| Time: 0:00:36 225.32 kB/s
numpy-1.13.1-p 100% |################################| Time: 0:00:04   1.68 MB/s
bottleneck-1.2 100% |################################| Time: 0:00:01 224.70 kB/s
h5py-2.7.0-np1 100% |################################| Time: 0:00:02   1.09 MB/s
numexpr-2.6.2- 100% |################################| Time: 0:00:01 288.12 kB/s
pywavelets-0.5 100% |################################| Time: 0:00:05   1.08 MB/s
scipy-0.19.1-n 100% |################################| Time: 0:01:25 459.82 kB/s
setuptools-36. 100% |################################| Time: 0:00:01 347.78 kB/s
pandas-0.20.3- 100% |################################| Time: 0:00:56 407.34 kB/s
pytables-3.4.2 100% |################################| Time: 0:00:41 168.51 kB/s
scikit-learn-0 100% |################################| Time: 0:01:19 158.86 kB/s
astropy-2.0.1- 100% |################################| Time: 0:00:15 644.67 kB/s
statsmodels-0. 100% |################################| Time: 0:00:44 178.04 kB/s
conda-4.3.27-p 100% |################################| Time: 0:00:00  44.12 MB/s
matplotlib-2.0 100% |################################| Time: 0:00:04   2.51 MB/s
scikit-image-0 100% |################################| Time: 0:02:18 245.94 kB/s


  - https://repo.continuum.io/pkgs/free/noarch
  - https://repo.continuum.io/pkgs/r/linux-ppc64le
  - https://repo.continuum.io/pkgs/r/noarch
  - https://repo.continuum.io/pkgs/pro/linux-ppc64le
  - https://repo.continuum.io/pkgs/pro/noarch


이제 pytorch를 설치할 차례입니다.

root@ubuntu:/data/pytorch# python setup.py install
Could not find /data/pytorch/torch/lib/gloo/CMakeLists.txt
Did you run 'git submodule update --init'?

억, git clone할 때 --recursive를 안 붙여줬기 때문에 이런 error를 겪나 봅니다.  시키는 대로 git submodule 명령을 수행하시면 이 error는 안 생깁니다.

root@ubuntu:/data/pytorch# git submodule update --init
Submodule 'torch/lib/gloo' (https://github.com/facebookincubator/gloo) registered for path 'torch/lib/gloo'
Submodule 'torch/lib/nanopb' (https://github.com/nanopb/nanopb.git) registered for path 'torch/lib/nanopb'
Submodule 'torch/lib/pybind11' (https://github.com/pybind/pybind11) registered for path 'torch/lib/pybind11'
Cloning into 'torch/lib/gloo'...
remote: Counting objects: 1922, done.
remote: Compressing objects: 100% (61/61), done.
remote: Total 1922 (delta 28), reused 64 (delta 24), pack-reused 1837
Receiving objects: 100% (1922/1922), 567.77 KiB | 0 bytes/s, done.
Resolving deltas: 100% (1422/1422), done.
Checking connectivity... done.
Submodule path 'torch/lib/gloo': checked out '7fd607e2852c910f0f1320d2aaa92f1da2291109'
Cloning into 'torch/lib/nanopb'...
remote: Counting objects: 4384, done.
...
Resolving deltas: 100% (6335/6335), done.
Checking connectivity... done.
Submodule path 'torch/lib/pybind11': checked out '9f6a636e547fc70a02fa48436449aad67080698f'


이제 다시 pytorch를 설치합니다.   보시다시피 설치 메시지에서 x86 아키텍처에만 있는 SSE2 extension이 없다든가, mkl_intel이 없다는 등의 경고 메시지가 많이 나옵니다만 대범하게 무시하십시요.

root@ubuntu:/data/pytorch# python setup.py install
running install
running build_deps
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Checking if C linker supports --verbose
-- Checking if C linker supports --verbose - yes
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Checking if CXX linker supports --verbose
-- Checking if CXX linker supports --verbose - yes
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Try OpenMP C flag = [-fopenmp]
-- Performing Test OpenMP_FLAG_DETECTED
-- Performing Test OpenMP_FLAG_DETECTED - Success
-- Try OpenMP CXX flag = [-fopenmp]
-- Performing Test OpenMP_FLAG_DETECTED
-- Performing Test OpenMP_FLAG_DETECTED - Success
-- Found OpenMP: -fopenmp
-- Compiling with OpenMP support
-- Could not find hardware support for NEON on this machine.
-- No OMAP3 processor on this machine.
-- No OMAP4 processor on this machine.
-- Looking for cpuid.h
...
-- Performing Test C_HAS_SSE1_1
-- Performing Test C_HAS_SSE1_1 - Failed
-- Performing Test C_HAS_SSE1_2
-- Performing Test C_HAS_SSE1_2 - Failed
-- Performing Test C_HAS_SSE1_3
...
-- Checking for [mkl_gf_lp64 - mkl_gnu_thread - mkl_core - gomp - pthread - m - dl]
--   Library mkl_gf_lp64: not found
-- Checking for [mkl_gf_lp64 - mkl_intel_thread - mkl_core - gomp - pthread - m - dl]
--   Library mkl_gf_lp64: not found
...
-- MKL library not found
-- Checking for [openblas]
--   Library openblas: /opt/anaconda2/lib/libopenblas.so
-- Looking for sgemm_
-- Looking for sgemm_ - found
-- Performing Test BLAS_F2C_DOUBLE_WORKS
-- Performing Test BLAS_F2C_DOUBLE_WORKS - Success
-- Performing Test BLAS_F2C_FLOAT_WORKS
-- Performing Test BLAS_F2C_FLOAT_WORKS - Success
-- Performing Test BLAS_USE_CBLAS_DOT
-- Performing Test BLAS_USE_CBLAS_DOT - Success
-- Found a library with BLAS API (open).
-- Looking for cheev_
-- Looking for cheev_ - found
-- Found a library with LAPACK API. (open)
-- Looking for clock_gettime in rt
-- Looking for clock_gettime in rt - found
...
Scanning dependencies of target TH
[ 12%] Building C object CMakeFiles/TH.dir/THSize.c.o
[ 12%] Building C object CMakeFiles/TH.dir/THHalf.c.o
[ 18%] Building C object CMakeFiles/TH.dir/THGeneral.c.o
[ 25%] Building C object CMakeFiles/TH.dir/THAllocator.c.o
[ 31%] Building C object CMakeFiles/TH.dir/THStorage.c.o
[ 37%] Building C object CMakeFiles/TH.dir/THRandom.c.o
[ 43%] Building C object CMakeFiles/TH.dir/THFile.c.o
[ 50%] Building C object CMakeFiles/TH.dir/THTensor.c.o
[ 56%] Building C object CMakeFiles/TH.dir/THDiskFile.c.o
[ 62%] Building C object CMakeFiles/TH.dir/THMemoryFile.c.o
[ 75%] Building C object CMakeFiles/TH.dir/THLogAdd.c.o
[ 75%] Building C object CMakeFiles/TH.dir/THLapack.c.o
[ 81%] Building C object CMakeFiles/TH.dir/THBlas.c.o
[ 87%] Building C object CMakeFiles/TH.dir/THVector.c.o
[ 93%] Building C object CMakeFiles/TH.dir/THAtomic.c.o
...
/data/pytorch/torch/lib/tmp_install/include/THC/THCNumerics.cuh(38): warning: integer conversion resulted in a change of sign
...
Compiling src/reduce_scatter.cu               > /data/pytorch/torch/lib/build/nccl/obj/reduce_scatter.o
ptxas warning : Too big maxrregcount value specified 96, will be ignored
ptxas warning : Too big maxrregcount value specified 96, will be ignored
...
byte-compiling /opt/anaconda2/lib/python2.7/site-packages/torch/utils/data/__init__.py to __init__.pyc
byte-compiling /opt/anaconda2/lib/python2.7/site-packages/torch/utils/data/dataset.py to dataset.pyc
byte-compiling /opt/anaconda2/lib/python2.7/site-packages/torch/utils/data/distributed.py to distributed.pyc
byte-compiling /opt/anaconda2/lib/python2.7/site-packages/torch/_utils.py to _utils.pyc
running install_egg_info
running egg_info
creating torch.egg-info
writing requirements to torch.egg-info/requires.txt
writing torch.egg-info/PKG-INFO
writing top-level names to torch.egg-info/top_level.txt
writing dependency_links to torch.egg-info/dependency_links.txt
writing manifest file 'torch.egg-info/SOURCES.txt'
reading manifest file 'torch.egg-info/SOURCES.txt'
writing manifest file 'torch.egg-info/SOURCES.txt'
Copying torch.egg-info to /opt/anaconda2/lib/python2.7/site-packages/torch-0.2.0+efe91fb-py2.7.egg-info
running install_scripts

보시다시피 warning message들이 나왔을 뿐 결국 잘 compile 됩니다.   이제 python에서 torch를 import하여 간단한 test를 몇가지 해보겠습니다.   저는 프로그래밍에 젬병인지라 그냥 pytorch 홈페이지의 튜토리얼 (http://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py) 일부를 그대로 수행해보았습니다.

root@ubuntu:/data/pytorch# python
Python 2.7.13 |Anaconda custom (64-bit)| (default, Mar 16 2017, 18:34:18)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org
>>> from __future__ import print_function
>>> import torch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "torch/__init__.py", line 53, in <module>
    from torch._C import *
ImportError: No module named _C
>>>

시작하자마자 이게 웬 error인가요 ?  이건 다소 우스운 error입니다.  (https://github.com/pytorch/pytorch/issues/7)  pytorch의 설치 directory (여기서는 /data/pytorch)에는 torch라는 이름의 directory가 있는데, 이 directory에서 import torch를 하면 이 directory name과 중복되어 error가 나는 것입니다.  그냥 다른 directory로 옮기셔서 python을 수행하시면 이 error는 나지 않습니다. 

root@ubuntu:/data/pytorch# ls
build            DLConvertor.h  LICENSE           test            tox.ini
cmake            dlpack.h       README.md         tools
CONTRIBUTING.md  Dockerfile     requirements.txt  torch
DLConvertor.cpp  docs           setup.py          torch.egg-info

이제 다른 아무 directory로 옮겨가서 거기서 python을 수행하겠습니다.

root@ubuntu:/data/pytorch# cd

root@ubuntu:~# python
Python 2.7.13 |Anaconda custom (64-bit)| (default, Mar 16 2017, 18:34:18)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org
>>> from __future__ import print_function
>>> import torch

예, 이번에는 이상없이 import됩니다.  Torch에서 사용하는 Tensor 및 rand 함수를 써보겠습니다.

>>> x = torch.Tensor(5, 3)
>>> print(x)

 0.0000e+00  0.0000e+00  0.0000e+00
 0.0000e+00  9.1957e+35  2.2955e-41
 9.2701e+35  2.2955e-41  1.1673e+36
 2.2955e-41  9.2913e+35  2.2955e-41
 0.0000e+00  0.0000e+00  0.0000e+00
[torch.FloatTensor of size 5x3]

>>> x = torch.rand(5, 3)
>>> print(x)

 0.7949  0.8651  0.0330
 0.5913  0.2181  0.9074
 0.7759  0.0349  0.9361
 0.3618  0.9953  0.8532
 0.2193  0.1514  0.6486
[torch.FloatTensor of size 5x3]

>>> print(x.size())
(5L, 3L)

>>> y = torch.rand(5, 3)
>>> print(x + y)

 0.8520  1.0601  0.7188
 0.7161  0.3146  1.0981
 1.4604  1.0081  0.9696
 1.1450  1.7239  1.2189
 0.2487  0.9476  1.6199
[torch.FloatTensor of size 5x3]

>>> print(torch.add(x, y))

 0.8520  1.0601  0.7188
 0.7161  0.3146  1.0981
 1.4604  1.0081  0.9696
 1.1450  1.7239  1.2189
 0.2487  0.9476  1.6199
[torch.FloatTensor of size 5x3]

>>> result = torch.Tensor(5, 3)
>>> torch.add(x, y, out=result)

 0.8520  1.0601  0.7188
 0.7161  0.3146  1.0981
 1.4604  1.0081  0.9696
 1.1450  1.7239  1.2189
 0.2487  0.9476  1.6199
[torch.FloatTensor of size 5x3]

>>> y.add_(x)

 0.8520  1.0601  0.7188
 0.7161  0.3146  1.0981
 1.4604  1.0081  0.9696
 1.1450  1.7239  1.2189
 0.2487  0.9476  1.6199
[torch.FloatTensor of size 5x3]

다 잘 됩니다.  Torch Tensor를 numpy Array로 전환하는 것도 해보겠습니다.

>>> a = torch.ones(5)
>>> print(a)

 1
 1
 1
 1
 1
[torch.FloatTensor of size 5]

>>> b = a.numpy()
>>> print(b)
[ 1.  1.  1.  1.  1.]

>>> a.add_(1)

 2
 2
 2
 2
 2
[torch.FloatTensor of size 5]

>>> print(b)
[ 2.  2.  2.  2.  2.]

다 잘 됩니다.   이제 GPU를 써서 pytorch를 구동해보겠습니다.

>>> if torch.cuda.is_available():
...     x = x.cuda()
...     y = y.cuda()
...     x + y
...

 1.6470  1.9252  0.7518
 1.3074  0.5327  2.0054
 2.2363  1.0430  1.9057
 1.5068  2.7193  2.0721
 0.4680  1.0990  2.2685
[torch.cuda.FloatTensor of size 5x3 (GPU 0)]

역시 잘 됩니다.  저 위의 .cuda()가 구동되는 순간 아래와 같이 GPU를 이미 python이 점거하는 것을 보실 수 있습니다.

Tue Oct 10 00:13:54 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 361.119                Driver Version: 361.119                   |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:03:00.0     Off |                    0 |
| N/A   44C    P0    58W / 149W |    200MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla K80           Off  | 0000:04:00.0     Off |                    0 |
| N/A   30C    P8    31W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   2  Tesla K80           Off  | 0020:03:00.0     Off |                    0 |
| N/A   35C    P8    26W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   3  Tesla K80           Off  | 0020:04:00.0     Off |                    0 |
| N/A   30C    P8    29W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     40655    C   python                                         198MiB |
+-----------------------------------------------------------------------------+


이제 가장 간단한 deep learning 예제인 MNIST를 pytorch로 수행해보겠습니다.   먼저, 아래와 같이 pytorch에서 제공하는 example source code를 download 받습니다.

root@ubuntu:/data# git clone --recursive https://github.com/pytorch/examples.git
Cloning into 'examples'...
remote: Counting objects: 1461, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 1461 (delta 1), reused 2 (delta 0), pack-reused 1455
Receiving objects: 100% (1461/1461), 29.95 MiB | 6.56 MiB/s, done.
Resolving deltas: 100% (767/767), done.
Checking connectivity... done.

root@ubuntu:/data# cd examples/mnist

다운받은 requirements.txt 속에는 아래와 같이 torch와 torchvision의 2줄이 들어있습니다.

root@ubuntu:/data/examples/mnist# vi requirements.txt
torch
torchvision

여기에 대해 pip로 install하면 다음과 같이 torchvision이 새로 설치됩니다.

root@ubuntu:/data/examples/mnist# pip install -r requirements.txt
Requirement already satisfied: torch in /opt/anaconda2/lib/python2.7/site-packages (from -r requirements.txt (line 1))
Collecting torchvision (from -r requirements.txt (line 2))
  Downloading torchvision-0.1.9-py2.py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 423kB/s
Requirement already satisfied: pyyaml in /opt/anaconda2/lib/python2.7/site-packages (from torch->-r requirements.txt (line 1))
Requirement already satisfied: numpy in /opt/anaconda2/lib/python2.7/site-packages (from torch->-r requirements.txt (line 1))
Requirement already satisfied: pillow in /opt/anaconda2/lib/python2.7/site-packages (from torchvision->-r requirements.txt (line 2))
Requirement already satisfied: six in /opt/anaconda2/lib/python2.7/site-packages (from torchvision->-r requirements.txt (line 2))
Requirement already satisfied: olefile in /opt/anaconda2/lib/python2.7/site-packages (from pillow->torchvision->-r requirements.txt (line 2))
Installing collected packages: torchvision
Successfully installed torchvision-0.1.9

이제 남은 것은 mnist directory에 들어있는 main.py를 수행하기만 하면 됩니다.

root@ubuntu:/data/examples/mnist# python main.py
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Processing...
...
Train Epoch: 10 [55040/60000 (92%)]     Loss: 0.148484
Train Epoch: 10 [55680/60000 (93%)]     Loss: 0.215679
Train Epoch: 10 [56320/60000 (94%)]     Loss: 0.122693
Train Epoch: 10 [56960/60000 (95%)]     Loss: 0.120907
Train Epoch: 10 [57600/60000 (96%)]     Loss: 0.153347
Train Epoch: 10 [58240/60000 (97%)]     Loss: 0.100982
Train Epoch: 10 [58880/60000 (98%)]     Loss: 0.272780
Train Epoch: 10 [59520/60000 (99%)]     Loss: 0.079338

Test set: Average loss: 0.0541, Accuracy: 9815/10000 (98%)

10번의 epoch(전체 training dataset을 총 10회 반복 training했다는 뜻) 만에 98.15%의 accuracy를 결과로 냅니다.   이 과정에서는 물론 GPU는 1장만 쓰는데, 대략 30~35%의 사용률을 보입니다.  (이거 P100이 아니라 K80입니다.  착오 없으시기 바랍니다.)


Tue Oct 10 00:23:02 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 361.119                Driver Version: 361.119                   |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:03:00.0     Off |                    0 |
| N/A   44C    P0    62W / 149W |    380MiB / 11441MiB |     30%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla K80           Off  | 0000:04:00.0     Off |                    0 |
| N/A   30C    P8    31W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   2  Tesla K80           Off  | 0020:03:00.0     Off |                    0 |
| N/A   36C    P8    26W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   3  Tesla K80           Off  | 0020:04:00.0     Off |                    0 |
| N/A   30C    P8    29W / 149W |      2MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     40735    C   python                                         378MiB |
+-----------------------------------------------------------------------------+

2017년 9월 15일 금요일

PowerAI 4.0의 DDL을 이용한 caffe와 tensorflow의 병렬처리

PowerAI 4.0에 포함된 DDL(Distributed Deep Learning)의 구체적인 사용법에 대해서 보시겠습니다.

일단 caffe는 IBM 버전 caffe (caffe-ibm)에 DDL 옵션이 통합되어 있으므로 별도 debian 패키지를 설치할 필요가 없습니다.  이 caffe-ibm도 내부적으로는 OpenMPI를 이용하는 것이므로 관련 library들이 설치되기는 해야 합니다만, 이는 caffe-ibm을 설치할 때 함께 자동으로 설치되므로 따로 신경쓰지 않으셔도 됩니다.

nimbix@JARVICENAE-0A0A1835:/data/mnist$ dpkg -l | grep openmpi
ii  libopenmpi2-cuda:ppc64el               2.0.1-4ibm1                                ppc64el      high performance message passing library -- shared library
ii  openmpi-bin-cuda                       2.0.1-4ibm1                                ppc64el      high performance message passing library -- binaries
ii  openmpi-common-cuda                    2.0.1-4ibm1                                all          high performance message passing library -- common files
ii  openmpi-doc-cuda                       2.0.1-4ibm1                                all          high performance message passing library -- man pages

가령 위에서 보는 것과 같이 CUDA-aware OpenMPI를 설치하고나면, mpirun이라는 MPI utility가 설치됩니다.  이 mpirun이라는 것은 여러단계의 soft link가 걸린 orterun이라는 명령어이고, 결국 아래와 같이 openmpi-bin-cuda에서 제공됩니다.

nimbix@JARVICENAE-0A0A1835:/data/mnist$ dpkg -S /usr/bin/orterun
openmpi-bin-cuda: /usr/bin/orterun

IBM 버전 caffe에서의 DDL 사용법은 알고 보면 단순합니다.  다음 4가지만 아시면 됩니다.

1) caffe 명령을 수행할 때 -ddl 옵션을 준다
2) Train/Validation용 dataset은 모든 서버에서 동일한 위치(directory)에 존재해야 한다  (병렬파일시스템 또는 NFS가 편리)
3) 모든 서버는 암호 없이 ssh가 되도록 ssh-keygen과 ssh-copy-id가 되어 있어야 한다
4) 환경변수 등을 다른 서버 노드에도 전달하기 위해서는 mpirun 명령을 사용하는 것이 편하다

다른 것은 다 쉽습니다만 1)번 항목이 조금 어렵게 느껴질 수도 있습니다.  복잡한 부분은 다 빼고, 그냥 쉽게 보면 이렇습니다.

DDL 옵션을 쓴다고 해서 caffe가 여러분이 가진 GPU서버 및 network 환경을 스스로 이해하고 그에 맞게 자동으로 최적화할 수는 없습니다.  따라서 그런 환경, 즉 topology를 caffe에게 여러분이 직접 알려주셔야 합니다.  그게 -ddl 옵션의 mode입니다.  쉽게 예를 들어 설명하면 다음과 같습니다.

$ mpirun -x PATH -x LD_LIBRARY_PATH -n 12 -rf 4x3.rf caffe train -solver /data/mnist/lenet_solver.prototxt -gpu 0 -bvlc -ddl "-mode n:4x3x1 -dev_sync 1"

- mpirun은 여러대의 서버 노드에 동일한 명령을 동일한 환경변수 (-x 옵션)을 써서 수행해주는 병렬환경 명령어입니다.
- 4x3.rf라는 이름의 파일은 rank file입니다.  이 속에 병렬 서버 환경의 toplogy가 들어있습니다.  이걸 어떻게 만드는지는 아래에서 다루겠습니다.
- -n 12라는 것은 MPI client의 총 숫자이며, 쉽게 말해 training에 이용하려는 GPU의 갯수입니다.
- -gpu 0에서, 왜 12개가 아니라 gpu 0이라고 1개로 지정했는지 의아하실 수 있는데, MPI 환경에서는 각각의 GPU가 하나의 learner가 됩니다.  따라서 실제 물리적 서버 1대에 GPU가 몇 장 장착되어있든 상관없이 모두 -gpu 0, 즉 GPU는 1개로 지정한 것입니다.
- "-mode n:4x3x1"에서 n이라는 것은 NCCL (NVIDIA Collective Communications Library, 니클이라고 읽습니다)을 이용하라는 뜻입니다.  4x3x1은 4장의 GPU를 가진 서버 3대가 하나의 rack에 들어있다는 뜻입니다.  사실 어느 rack에 들어있느냐가 중요한 것은 아닌데, 보통 병렬수퍼컴 환경에서는 한대의 rack 안에 장착된 서버끼리는 좀더 고속의 low latency network으로 연결되어있기 때문에 이렇게 rack 표시까지 해주는 것입니다.  만약 4장의 GPU를 가진 서버가 6대씩 장착된 rack이 5대있다면 4x6x5로 표시됩니다.
- dev_sync에서 0은 GPU간 sync를 하지 말라는 것이고, 1은 통신 시작할 때 sync하라는 뜻, 2는 시작할 때와 끝낼 때 각각 sync하라는 뜻입니다.

잠깐, 데이터는 어디에 있는지 어떻게 지정하냐고요 ?  저 위에 지정된 solver 파일, 즉 lenet_solver.prototxt에 neural network이 지정되어 있고, 다시 그 neural network의 prototxt 파일 속에 데이터 위치가 지정되어 있습니다.   아래처럼요.

$ vi lenet_solver.prototxt
#net: "examples/mnist/lenet_train_test.prototxt"
net: "/data/mnist/lenet_train_test.prototxt"

$ vi lenet_train_test.prototxt
...
#    source: "examples/mnist/mnist_train_lmdb"
    source: "/data/mnist/mnist_train_lmdb"
...
#    source: "examples/mnist/mnist_test_lmdb"
    source: "/data/mnist/mnist_test_lmdb"

여러 서버 노드들의 GPU마다 수행될 learner들이 어떻게 data를 나누어 가져가느냐고요 ?  가급적이면 서버 노드마다 미리 파티셔닝되어 적절히 분배된 data들을 넣어두는 것이 좋습니다.  Data를 N개의 learner들이 읽어갈 때, 각자 순차적으로 파일 이름들이 뒤섞여 들어간 목록으로부터 data를 읽어가는데, 만약 이 data가 물리적으로 미리 파티셔닝하여 노드 별로 분배해놓은 것이 아니라면 그냥 1번 training을 끝낼 때마다 전체 data를 N번 (N epochs) training한 것과 같은 효과를 냅니다.   저 data들의 저장소는 여러 노드에서 동시에 access할 수 있도록 IBM Spectrum Scale (구명칭 GPFS) 같은 병렬 파일시스템으로 하든가, 그게 없다면 성능이 떨어지더라도 NFS 같은 것으로 구성하는 것이 좋습니다.

이제 저 rf 파일, 즉 랭크 파일을 어떻게 만드는지 보시겠습니다.  그냥 손으로, 즉 vi 에디터 같은 것을 이용해서 만드셔도 됩니다만, PowerAI에서 기본 제공되는 rank_gen.py를 이용해서 다음과 같이 만드시는 것이 편합니다.

$ python /opt/DL/ddl/bin/rank_gen.py 4x2x3 sys-89074,sys-89075,sys-89076,sys-89077,sys-89078,sys-89079 > 4x2x3.rf

위에서 콤마(,)로 분리된 이름들이 서버 이름들입니다.  4x2x3이니 4장의 GPU를 가진 서버가 총 6대 있는 것이니, 서버 이름은 반드시 6대를 적으셔야 합니다.   이렇게 만들어진 4x2x3.rf 파일의 내용은 아래와 같습니다.  rank_gen.py는 기본적으로 10-core POWER8 chip 2장을 장착한 Minsky 서버를 기준으로 만들기 때문에 아래와 같이 10개의 core를 가진 slot 2개가 있는 것으로 나옵니다.  그래서 rank, 즉 GPU 1개마다 slot이 5개 (0-4) 있는 것으로 나오는데, 만약 그게 아니라 8-core POWER8 chip이 장착된 서버라면 수작업으로 0-4가 아닌 0-3으로 수정해주셔야 합니다.

u0017649@sys-89075:~$ cat 4x2x3.rf
#2017-09-14 04:45:51 by rank_gen
#dims = 4x2x3
#host = sys-89074,sys-89075,sys-89076,sys-89077,sys-89078,sys-89079
#dimX = 4
#dimY = 2
#dimZ = 3
#sockets = 2
#cores = 10

rank 0=sys-89074           slot=0:0-4
rank 6=sys-89074           slot=0:5-9
rank 12=sys-89074          slot=1:0-4
rank 18=sys-89074          slot=1:5-9

rank 3=sys-89075           slot=0:0-4
rank 9=sys-89075           slot=0:5-9
rank 15=sys-89075          slot=1:0-4
rank 21=sys-89075          slot=1:5-9


rank 1=sys-89076           slot=0:0-4
rank 7=sys-89076           slot=0:5-9
rank 13=sys-89076          slot=1:0-4
rank 19=sys-89076          slot=1:5-9

rank 4=sys-89077           slot=0:0-4
rank 10=sys-89077          slot=0:5-9
rank 16=sys-89077          slot=1:0-4
rank 22=sys-89077          slot=1:5-9


rank 2=sys-89078           slot=0:0-4
rank 8=sys-89078           slot=0:5-9
rank 14=sys-89078          slot=1:0-4
rank 20=sys-89078          slot=1:5-9

rank 5=sys-89079           slot=0:0-4
rank 11=sys-89079          slot=0:5-9
rank 17=sys-89079          slot=1:0-4
rank 23=sys-89079          slot=1:5-9



Caffe는 그렇게 쉽게 됩니다만, tensorflow는 그보다 좀 어렵습니다.  일단 별도의 ddl-tensorflow라는 debian package가 PowerAI 4.0에 포함되어 있는데, 이는 사실 tensorflow DDL에 꼭 필요한 것이 아니라, tensorflow DDL을 좀더 쉽게 사용하실 수 있도록 해주는 Google Slim에 기반한 script들과 example 파일들을 제공해주는 것입니다.  정작 tensorflow는 별도로 설치하셔야 하는데, 물론 그건 PowerAI에서 제공되는 tensorflow를 apt-get install 명령으로 설치하시면 됩니다.

$ sudo apt-get install ddl-tensorflow tensorflow

$ dpkg -L ddl-tensorflow
/.
/opt
/opt/DL
/opt/DL/ddl-tensorflow
/opt/DL/ddl-tensorflow/examples
/opt/DL/ddl-tensorflow/examples/mnist
/opt/DL/ddl-tensorflow/examples/mnist/ddl_mnist.py
/opt/DL/ddl-tensorflow/examples/mnist/README.md
/opt/DL/ddl-tensorflow/examples/slim
/opt/DL/ddl-tensorflow/examples/slim/BUILD
/opt/DL/ddl-tensorflow/examples/slim/WORKSPACE
/opt/DL/ddl-tensorflow/examples/slim/scripts
/opt/DL/ddl-tensorflow/examples/slim/scripts/finetune_inception_resnet_v2_on_flowers.sh
/opt/DL/ddl-tensorflow/examples/slim/scripts/train_lenet_on_mnist.sh
/opt/DL/ddl-tensorflow/examples/slim/scripts/finetune_resnet_v1_50_on_flowers.sh
/opt/DL/ddl-tensorflow/examples/slim/scripts/finetune_inception_v3_on_flowers.sh
/opt/DL/ddl-tensorflow/examples/slim/scripts/train_cifarnet_on_cifar10.sh
/opt/DL/ddl-tensorflow/examples/slim/scripts/finetune_inception_v1_on_flowers.sh
/opt/DL/ddl-tensorflow/examples/slim/train-alexnet.sh
/opt/DL/ddl-tensorflow/examples/slim/deployment
/opt/DL/ddl-tensorflow/examples/slim/deployment/__init__.py
...

이 ddl-tensorflow를 사용하시기 위해서는 PYTHONPATH 등의 환경변수 설정을 위해 source 명령으로 아래와 같이 ddl-tensorflow-activate를 수행해주셔야 합니다.

$ source /opt/DL/ddl-tensorflow/bin/ddl-tensorflow-activate

이제 ddl-tensorflow-install-samples 명령을 사용하시어 지정하는 directory에 sample들을 설치하실 수 있습니다.

nimbix@JARVICENAE-0A0A1835:~$ ddl-tensorflow-install-samples /data
Write into existing directory /data? (yN)
y
Copying examples/ into /data...
Success

가장 간단한 것으로, 손글씨 숫자를 판독하는 MNIST가 들어 있습니다.

nimbix@JARVICENAE-0A0A1835:~$ cd /data/examples/mnist

nimbix@JARVICENAE-0A0A1835:/data/examples/mnist$ ls
ddl_mnist.py  README.md

여기에 나온 것처럼, tensorflow는 명령어라기보다는 python에서 불러 사용하는 library로 되어 있기 때문에, 결국 multi-node 병렬처리를 하기 위해서는 python script를 위의 ddl_mnist.py에서처럼 작성해야 합니다.

일단 4-GPU 서버 2대(sys-89074와 sys-89075)로 수행하는 환경이라고 가정하고 아래와 같이 rank file을 먼저 만듭니다.

nimbix@JARVICENAE-0A0A1835:/data/examples/mnist$ python /opt/DL/ddl/bin/rank_gen.py 4x2x1 sys-89074,sys-89075 > 4x2.rf

nimbix@JARVICENAE-0A0A1835:/data/examples/mnist$ cat 4x2.rf
#2017-09-15 03:19:14 by rank_gen
#dims = 4x2x1
#host = sys-89074,sys-89075
#dimX = 4
#dimY = 2
#dimZ = 1
#sockets = 2
#cores = 10

rank 0=sys-89074           slot=0:0-4
rank 2=sys-89074           slot=0:5-9
rank 4=sys-89074           slot=1:0-4
rank 6=sys-89074           slot=1:5-9

rank 1=sys-89075           slot=0:0-4
rank 3=sys-89075           slot=0:5-9
rank 5=sys-89075           slot=1:0-4
rank 7=sys-89075           slot=1:5-9


이제 다음과 같이 수행하면 됩니다.

nimbix@JARVICENAE-0A0A1835:/data/examples/mnist$ mpirun -x PATH -x LD_LIBRARY_PATH -x PYTHONPATH -n 8 -rf 4x2.rf python ddl_mnist.py

(사실 mnist는 워낙 작은 dataset만 사용하므로, 병렬화의 의미가 없습니다.  그래서인지 ddl_mnist.py는 위에서 제가 예로 든 것처럼 4x2 구조는 애초에 불가능하고, 저 아래에 보시듯이 -mode r:2로 되어 있어 그냥 GPU 2장으로 병렬화하는 것만 가능합니다.)

결국 문제는 tensorflow를 병렬로 수행하기 위해서 python script를 어떻게 작성해야 하느냐인데, 이 부분에 대해서는 저도 개발자가 아닌 관계로 별 도움을 못 드리겠습니다.  (사실 제겐 흰건 글씨요 검은건 공백이며, 깜빡이는 것은 커서 정도로만 보입니다.)

대신, 다소 깁니다만, 아래에 PowerAI에 포함된 ddl_mnist.py의 내용을 그대로 올려두겠습니다.

nimbix@JARVICENAE-0A0A1835:/data/examples/mnist$ vi ddl_mnist.py

import tensorflow as tf
import numpy as np

############################################################################
#   IBM PowerAI Distributed Deep Learning (DDL) setup
############################################################################

# Disable GPU memory preallocation
config = tf.ConfigProto()
config.gpu_options.allow_growth = True

############################################################################
#   DDL Initialize BEGIN
############################################################################
# Load DDL operator
ddl = tf.load_op_library('/opt/DL/ddl-tensorflow/lib/ddl_MDR.so')


# DDL initializes MPI on CPU
# ddl.init takes two inputs
# 1) the number of GPUs to utilize on each host in training.
#    this number is not the number of GPUs to use for each leaner. It simply tells DDL that there are X GPUs in each host to be used for training
# 2) DDL options (refer to README for details)
with tf.Session(config=config) as sess:
    with tf.device('/cpu:0'):
        rank, size, gpuid = sess.run(ddl.init(2, mode = '-mode r:2 -dump_iter 100'))

# MPI info and assigned GPU
print [rank, size, gpuid]
############################################################################
#   DDL Initialize END
############################################################################


# Perform all TensorFlow computation within gpuid
with tf.device('/gpu:%d' %gpuid):
    ##############################################################################
    # Import MNIST data

    from tensorflow.examples.tutorials.mnist import input_data
    mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)

    # Parameters
    learning_rate = 0.001
    training_iters = 200000
    batch_size = 100
    display_step = 1

    # Network Parameters
    n_input = 784 # MNIST data input (img shape: 28*28)
    n_classes = 10 # MNIST total classes (0-9 digits)
    dropout = 0.75 # Dropout, probability to keep units

    # tf Graph input
    x = tf.placeholder(tf.float32, [None, n_input])
    y = tf.placeholder(tf.float32, [None, n_classes])
    keep_prob = tf.placeholder(tf.float32) #dropout (keep probability)


    # Create some wrappers for simplicity
    def conv2d(x, W, b, strides=1):
        # Conv2D wrapper, with bias and relu activation
        x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding='SAME')
        x = tf.nn.bias_add(x, b)
        return tf.nn.relu(x)


    def maxpool2d(x, k=2):
        # MaxPool2D wrapper
        return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1],
                              padding='SAME')


    # Create model
    def conv_net(x, weights, biases, dropout):
        # Reshape input picture
        x = tf.reshape(x, shape=[-1, 28, 28, 1])

        # Convolution Layer
        conv1 = conv2d(x, weights['wc1'], biases['bc1'])
        # Max Pooling (down-sampling)
        conv1 = maxpool2d(conv1, k=2)

        # Convolution Layer
        conv2 = conv2d(conv1, weights['wc2'], biases['bc2'])
        # Max Pooling (down-sampling)
        conv2 = maxpool2d(conv2, k=2)

        # Fully connected layer
        # Reshape conv2 output to fit fully connected layer input
        fc1 = tf.reshape(conv2, [-1, weights['wd1'].get_shape().as_list()[0]])
        fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1'])
        fc1 = tf.nn.relu(fc1)
        # Apply Dropout
        fc1 = tf.nn.dropout(fc1, dropout)

        # Output, class prediction
        out = tf.add(tf.matmul(fc1, weights['out']), biases['out'])
        return out


    # Store layers weight & bias
    weights = {
        ############################################################################
        #   DDL BROADCAST BEGIN
        ############################################################################
        # This step ensures that all learners start with the same initial parameters

        # 5x5 conv, 1 input, 32 outputs
        'wc1': tf.Variable(ddl.bcast(tf.random_normal([5, 5, 1, 32]))),
        # 5x5 conv, 32 inputs, 64 outputs
        'wc2': tf.Variable(ddl.bcast(tf.random_normal([5, 5, 32, 64]))),
        # fully connected, 7*7*64 inputs, 1024 outputs
        'wd1': tf.Variable(ddl.bcast(tf.random_normal([7*7*64, 1024]))),
        # 1024 inputs, 10 outputs (class prediction)
        'out': tf.Variable(ddl.bcast(tf.random_normal([1024, n_classes])))
        ############################################################################
        #   DDL BROADCAST END
        ############################################################################
    }

    biases = {
        'bc1': tf.Variable(ddl.bcast(tf.random_normal([32]))),
        'bc2': tf.Variable(ddl.bcast(tf.random_normal([64]))),
        'bd1': tf.Variable(ddl.bcast(tf.random_normal([1024]))),
        'out': tf.Variable(ddl.bcast(tf.random_normal([n_classes])))
    }

    # Construct model
    pred = conv_net(x, weights, biases, keep_prob)

    # Define loss and optimizer
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
    optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)


    ############################################################################
    #   DDL ALLREDUCE BEGIN
    ############################################################################

    # Collect the gradients and the corresponding parameters w.r.t the given cost
    grads_and_vars = optimizer.compute_gradients(cost)

    # Separate out the tuple
    grads, vars = zip(*grads_and_vars)

    # This step takes the average of the gradients on all the learners
    grads_and_vars_ddl = zip(ddl.all_reduce_n(grads, op='avg'), vars)

    # Update the parameters with the averaged gradient
    objective = optimizer.apply_gradients(grads_and_vars_ddl)

    ############################################################################
    #   DDL ALLREDUCE END
    ############################################################################

    # Evaluate model
    correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
    ##############################################################################

def split(a, n):
    k, m = divmod(len(a), n)
    return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in xrange(n))

# Launch the graph
with tf.Session(config=config) as sess:
    sess.run(tf.global_variables_initializer())
    step = 1
    # Keep training until reach max iterations
    while step * batch_size < training_iters:

        # Each learner will read batch_size*size samples and
        # use only the portion correspoding to the current learner (or rank)

        batch_x, batch_y = mnist.train.next_batch(batch_size*size)

        batch_x = np.split(batch_x,size)[rank]
        batch_y = np.split(batch_y,size)[rank]

        # Run optimization op (backprop)
        sess.run(objective, feed_dict={x: batch_x, y: batch_y,
                                       keep_prob: dropout})
        if step % display_step == 0:
            # Calculate batch loss and accuracy
            loss, acc = sess.run([cost, accuracy], feed_dict={x: batch_x,
                                                              y: batch_y,
                                                              keep_prob: 1.})
            print("MPI "+str(rank)+"] Iter " + str(step*batch_size) + ", Minibatch Loss= " + \
                  "{:.6f}".format(loss) + ", Training Accuracy= " + \
                  "{:.5f}".format(acc))
        step += 1

    print("MPI "+str(rank)+"] Optimization Finished!")

    # Calculate accuracy for 256 mnist test images
    print("MPI "+str(rank)+"] Testing Accuracy:", \
        sess.run(accuracy, feed_dict={x: mnist.test.images[:256],
                                      y: mnist.test.labels[:256],
                                      keep_prob: 1.}))

Minsky 서버에 Anaconda installer의 full package 설치하기

올 3월까지만 해도 Continuum사의 Anaconda installer가 Minsky의 아키텍처인 ppc64le 환경에서는 full package로는 없었고, Miniconda installer만 있었습니다.   그런데 그 사이에 ppc64le용 Anaconda installer의 full package가 이미 나왔네요.

https://repo.continuum.io/archive/

Filename Size Last Modified MD5
Anaconda2-4.4.0.1-Linux-ppc64le.sh 271.4M 2017-07-26 16:10:02 ce166de6f116acd08cd313f9c55c04d6
Anaconda3-4.4.0.1-Linux-ppc64le.sh 285.6M 2017-07-26 16:08:42 fe7c87abd9fd70dc0cb4f83cc22d336f


설치는 아래와 같이 하시면 됩니다.  Python2.7 환경에서는 아래와 같이 Anaconda2-4.4.0.1-Linux-ppc64le.sh를 받으시면 되고, Python3 환경에서는 Anaconda3-4.4.0.1-Linux-ppc64le.sh를 받으시면 됩니다.  일단 둘다 받아서 설치하시는 것이 좋습니다.  여기서는 Anaconda2에 대한 부분만 써놓습니다.

u0017649@sys-89075:~$ wget https://repo.continuum.io/archive/Anaconda2-4.4.0.1-Linux-ppc64le.sh
--2017-09-14 21:42:44--  https://repo.continuum.io/archive/Anaconda2-4.4.0.1-Linux-ppc64le.sh
Resolving repo.continuum.io (repo.continuum.io)... 104.16.18.10, 104.16.19.10, 2400:cb00:2048:1::6810:120a, ...
Connecting to repo.continuum.io (repo.continuum.io)|104.16.18.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 284629257 (271M) [application/x-sh]
Saving to: ‘Anaconda2-4.4.0.1-Linux-ppc64le.sh’

Anaconda2-4.4.0.1-L 100%[===================>] 271.44M  16.7MB/s    in 17s

2017-09-14 21:43:01 (16.4 MB/s) - ‘Anaconda2-4.4.0.1-Linux-ppc64le.sh’ saved [284629257/284629257]


여러 user가 함께 쓰는 서버 환경에 Anaconda를 설치할 때는, 해당 user의 home directory에 설치하면 다른 user들이 쓸 수가 없지요.  따라서 일단은 /opt/anaconda2에 설치해놓고 user별로 clone을 해서 사용하시는 것이 좋습니다.   물론 /opt directory에 설치하려면 sudo 권한이 있어야 합니다.

u0017649@sys-89075:~$ chmod u+x Anaconda2-4.4.0.1-Linux-ppc64le.sh

u0017649@sys-89075:~$ sudo ./Anaconda2-4.4.0.1-Linux-ppc64le.sh

Do you approve the license terms? [yes|no]
>>> yes

Anaconda2 will now be installed into this location:
/home/u0017649/anaconda2

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/home/u0017649/anaconda2] >>> /opt/anaconda2

...
installing: zict-0.1.2-py27_0 ...
installing: zlib-1.2.8-3 ...
installing: anaconda-4.4.0-np112py27_0 ...
installing: conda-4.3.21-py27_0 ...
installing: conda-env-2.6.0-0 ...
Python 2.7.13 :: Continuum Analytics, Inc.
creating default environment...
installation finished.
Do you wish the installer to prepend the Anaconda2 install location
to PATH in your /home/u0017649/.bashrc ? [yes|no]
[no] >>> yes

Prepending PATH=/opt/anaconda2/bin to PATH in /home/u0017649/.bashrc
A backup will be made to: /home/u0017649/.bashrc-anaconda2.bak

For this change to become active, you have to open a new terminal.

Thank you for installing Anaconda2!

Share your notebooks and packages on Anaconda Cloud!
Sign up for free: https://anaconda.org


이제 환경변수를 적용하고, conda install을 해봅니다.

u0017649@sys-89075:~$ . ./.bashrc

u0017649@sys-89075:~$ env | grep PATH
PATH=/opt/anaconda2/bin:/home/u0017649/bin:/home/u0017649/.local/bin:/opt/ibm/xlC/current/bin:/opt/ibm/xlf/current/bin:/opt/at10.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

u0017649@sys-89075:~$ conda install scikit-learn
Fetching package metadata .........
Solving package specifications: .

Package plan for installation in environment /opt/anaconda2:

The following packages will be UPDATED:

    scikit-learn: 0.18.1-np112py27_1 --> 0.19.0-np112py27_0

Proceed ([y]/n)? y


CondaIOError: Missing write permissions in: /opt/anaconda2
#
# You don't appear to have the necessary permissions to install packages
# into the install area '/opt/anaconda2'.
# However you can clone this environment into your home directory and
# then make changes to it.
# This may be done using the command:
#
# $ conda create -n my_root --clone="/opt/anaconda2"


보시다시피 /opt/anaconda2에 대해서 일반 user들은 write 권한이 없기 때문에 error가 납니다.   그래서 각 user별로 cloning을 해서 사용하는 것입니다.  아래와 같이 하시면 됩니다.

u0017649@sys-89075:~$ conda create -n anaconda2 --clone="/opt/anaconda2"
Source:      /opt/anaconda2
Destination: /home/u0017649/.conda/envs/anaconda2
The following packages cannot be cloned out of the root environment:
 - conda-4.3.21-py27_0
 - conda-env-2.6.0-0
Packages: 186
Files: 11
#
# To activate this environment, use:
# > source activate anaconda2
#
# To deactivate this environment, use:
# > source deactivate anaconda2
#

이제 cloning 환경으로 들어가서 위에서 실패했던 scikit-learn 패키지의 설치를 다시 해보겠습니다.

u0017649@sys-89075:~$ source activate anaconda2
(anaconda2) u0017649@sys-89075:~$ conda install scikit-learn

이건 잘 됩니다.  이것이 잘 되는 이유는 다음과 같이 각 user의 home directory 밑인 ~/.conda/envs에 cloning을 해놓고 쓰기 때문입니다.   물론 이는 disk 저장공간을 사용하는 작업이므로, 많은 user들이 각자 모두 cloning을 할 경우 disk 공간의 낭비가 꽤 있게 되는 문제가 있긴 합니다.  가령 Anaconda2를 1번 clone할 때마다 약 1.1 GB의 공간이 소모됩니다.  따라서 모든 user가 일괄적으로 이렇게 cloning을 할 필요는 없고, 정말 꼭 자신만의 변경된 package 환경을 가져야 할 경우에만 하실 것을 권고합니다.

(anaconda2) u0017649@sys-89075:~/.conda/envs/anaconda2$ du -sm .
1135    .

2017년 9월 13일 수요일

IBM PowerAI 4.0에 포함된 Caffe Distributed Deep Learning (DDL)


Tensorflow는 distributed tensorflow를 예전부터 지원하여, 여러대의 서버에 장착된 여러장의 GPU를 이용한 분산 training이 가능했습니다.  IBM PowerAI toolkit에도 ddl-tensorflow가 포함되어 있습니다.

Caffe는 tensorflow와는 달리 분산 모델이 정식으로는 지원되지 않아 한대의 서버에서만 training이 가능했습니다.  물론 각 기업이나 연구소별로 open source로 공개되지 않은 자체적인 버전의 distributed caffe를 자체 개발하여 사용하고 있긴 했습니다.

최근 새로 나온 IBM PowerAI 4.0에 포함된 IBM 버전의 caffe에서는 Distributed Deep Learning (DDL) 옵션을 지원합니다.  이는 OpenMPI 기술에 기반하여 caffe가 하나의 큰 모델을 여러대의 서버에서 분산 처리할 수 있도록 만든 것입니다.  

구체적으로는 caffe 명령어에 -ddl 옵션이 추가된 형태로 제공됩니다.   구체적인 내용은 아래 link에 설명되어 있습니다.

https://public.dhe.ibm.com/software/server/POWER/Linux/mldl/ubuntu/README.html

문제는 여기에 설명되는 option parameter들에 대해, 충분한 설명이 없다는 것입니다.  가령 -ddl "-mode b:4x3"이라고 쓸 때, b는 뭐고 4는 무엇이며 3은 무엇인지 위 link만 보고는 알기가 어렵습니다.

-ddl "-mode b:4x3"를 설명하자면 b는 enhanced NCCL library를 쓰되, 4장의 GPU를 장착한 서버 3대를 쓰라는 것입니다.

또 가령 -ddl "-mode r:2x8"이라는 것은 RING 구성만 써서 2장의 GPU를 장착한 서버 8대를 쓰라는 것이고요.

이에 대해서 설명이 없는 이유를 IBM 본사에 물어보니, "인터넷에는 없지만 PowerAI 4.0을 설치하면 민스키 서버 안에 생성되는 /opt/DL/ddl/doc/README.md 파일 속에 설명이 다 들어있다" 라고 합니다.

해서, 많은 분들이 쉽게 보실 수 있도록 제가 여기에 그 파일 내용을 올려둡니다.


# Overview

IBM PowerAI Distributed Deep Learning (or DDL) is a MPI-based
communication library, which is specifically optimized for Deep Learning
training.  An application integrated with DDL becomes a MPI-application,
which will allow the use of the `mpirun` command to invoke the job in
parallel across a cluster of systems. DDL understands multi-tier network
environment and uses different libraries (e.g. NCCL) and algorithms to
get the best performance in multi-node, multi-GPU environments.

IBM PowerAI Distributed Deep Learning considers each GPU in a cluster as
an individual "learner".  The overall set of learners is described to
IBM PowerAI Distributed Deep Learning in terms of 3 dimensions (X-Y-Z)
that correspond to a multi-tier network hierarchy.  The recommended
mapping is:

   - X for within-host (e.g. number of GPUs per host for multi-GPU hosts)
   - Y for between nearby-hosts (e.g. number of hosts in a single rack)
   - Z for between distant-hosts (e.g. number of racks)

For example, 256 learners can be configured as 4x8x8 or 4x16x4 and so on.

**Example: 2 racks of 8 S822LC for HPC systems with 4 GPUs each**

In this setup, there are 64 learners (4 GPUs in each of 8 hosts in each
of 2 racks) and a simple description would be 4x8x2.

If this configuration includes a truly hierarchical network setup--for
example a high-speed, low-latency network within each rack, and a
slower, higher-latency network between the racks--then 4x8x2 might be
the optimal description.

But if the network configuration is not actually hierarchical--if all
the hosts are connected to a "flat" network regardless of the physical
racking--then a 4x4x4 description may perform better than 4x8x2. Some
experimentation may be needed to find the optimal description.


# Required Libraries

Pre-requisite packages required for IBM PowerAI Distributed Deep
Learning are provided with PowerAI:

   1. OpenMPI with CUDA Support
   2. NVIDIA NCCL


# Integration with Caffe and TensorFlow

IBM PowerAI Distributed Deep Learning has been integrated with the
PowerAI IBM Caffe and TensorFlow packages. `mpirun` must be used to
launch training using the IBM PowerAI Distributed Deep Learning
integration.  General information about `mpirun` is available on the
OpenMPI website
[https://www.open-mpi.org/doc/v2.0/man1/mpirun.1.php](https://www.open-mpi.org/doc/v2.0/man1/mpirun.1.php).

   1. Caffe

      IBM PowerAI Distributed Deep Learning is directly integrated into
      Caffe, and can be exercised by adding the following to the command line.

           -ddl ?쏡DL_OPTIONS HERE??
   2. TensorFlow

      DDL is indirectly integrated into TensorFlow in the form of custom
      operator. The custom operator is provided as a shared library, which is
      loaded and invoked in the python training script.

      The PowerAI ddl-tensorflow package provides an example training
      setup based on the TensorFlow-Slim model library from the TensorFlow
      models repository. Those can be found on your system in:

           /opt/DL/ddl-tensorflow/examples/

      More details on IBM PowerAI Distributed Deep Learning integration
      into TensorFlow, can be found in

           /opt/DL/ddl-tensorflow/doc/README.md


# Using IBM PowerAI Distributed Deep Learning

IBM PowerAI Distributed Deep Learning takes advantage of the network
topology to perform communication quickly. Network topology is described
to IBM PowerAI Distributed Deep Learning in two ways, through an MPI
rank file and via DDL options.

## MPI rank file

A rank file is a standard file that maps MPI clients (IBM PowerAI
Distributed Deep Learning learners) to specific hosts, sockets, and
cores. To get the best performance from IBM PowerAI Distributed Deep
Learning , it is crucial to generate an optimally mapped rank file. To
help with this effort, a script (`rank_gen.py`) is provided to
automatically generate rank files that are appropriate for most S822LC
systems. The script takes two inputs: the network decomposition and a
list of comma-separated hostnames.

**How to use rank file generator script**

        $ python rank_gen.py XxYxZ host_list > rank_file

Here, `XxYxZ` specifies the topology of the GPU and multi-tier network
hierarchy.

For example, for 64 learners (e.g. 16 hosts each with 4 GPUs), any of
4x16x1, 4x8x2, or 4x4x4 might be reasonable choices, depending on the
network topology. All 3 dimensions must be specificed (use 1 to fill any
spaces).

`host_list` is a comma separated list of host names (e.g.
host1,host2,host3,...).  It must contain `Y` times `Z` hostnames,
ordered "by Z". For example, a 4x2x2 configuration with 2 racks of 2
hosts each might have a host list of: `r1h1,r1h2,r2h1,r2h2`.The
hostnames provided in the rankfile should match the system hostnames.

It is possible in a distributed environment to have more than one
interface for each host. In such a scenario, OpenMPI by default, uses
any and all interfaces that are "up" to communicate with a host. To
avoid problems in such cases you can tell OpenMPI to use given
interface. E.g.:

        $ mpirun --mca btl_tcp_if_include ib0 ...

        $ mpirun --mca btl_tcp_if_exclude lo,enp1s0f2 ...

More details available on OpenMPI FAQ page:
[https://www.open-mpi.org/faq/?category=tcp#tcp-selection]([https://www.open-mpi.org/faq/?category=tcp#tcp-selection)

**Parameters for optimal rankfile**

An optimal rank file will depend on the number of sockets or nodes in
the system and the number of cores per socket/node. The `numactl` and
`ppc64_cpu` commands can help determine this information.

   1. Number of sockets and thread slots for each socket.

      `numactl -H` shows the number of sockets ("nodes") in a system,
      and also lists the CPUs (thread slots) for each. For example:

           $ numactl -H
           available: 2 nodes (0-1)
           node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
           node 0 size: 261788 MB
           node 0 free: 6042 MB
           node 1 cpus: 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
           node 1 size: 261334 MB
           node 1 free: 158805 MB
           node distances:
           node   0   1
             0:  10  40
             1:  40  10

      Here the system has two sockets with 80 thread slots each.

   2. Mapping between physical cores and CPUs/thread slots.

           $ ppc64_cpu --info
           Core   0:    0*    1*    2*    3*    4*    5*    6*    7*
           Core   1:    8*    9*   10*   11*   12*   13*   14*   15*
           Core   2:   16*   17*   18*   19*   20*   21*   22*   23*
           Core   3:   24*   25*   26*   27*   28*   29*   30*   31*
           Core   4:   32*   33*   34*   35*   36*   37*   38*   39*
           Core   5:   40*   41*   42*   43*   44*   45*   46*   47*
           Core   6:   48*   49*   50*   51*   52*   53*   54*   55*
           Core   7:   56*   57*   58*   59*   60*   61*   62*   63*
           Core   8:   64*   65*   66*   67*   68*   69*   70*   71*
           Core   9:   72*   73*   74*   75*   76*   77*   78*   79*
           Core  10:   80*   81*   82*   83*   84*   85*   86*   87*
           Core  11:   88*   89*   90*   91*   92*   93*   94*   95*
           Core  12:   96*   97*   98*   99*  100*  101*  102*  103*
           Core  13:  104*  105*  106*  107*  108*  109*  110*  111*
           Core  14:  112*  113*  114*  115*  116*  117*  118*  119*
           Core  15:  120*  121*  122*  123*  124*  125*  126*  127*
           Core  16:  128*  129*  130*  131*  132*  133*  134*  135*
           Core  17:  136*  137*  138*  139*  140*  141*  142*  143*
           Core  18:  144*  145*  146*  147*  148*  149*  150*  151*
           Core  19:  152*  153*  154*  155*  156*  157*  158*  159*

      Here the system has 20 physical cores with 8 thread slots/CPUs each. The
      thread slot numbers match with the numbers in the `numactl` output. The
      asterisks indicate which thread slots are enabled.

      The rankfile only cares about cores (not CPUs/thread slots), and the
      core numbering is relative to the to the node/socket (which is named
      "slot" in the rankfile). So in rank file terms, this system has socket 0
      cores 0-9 and socket 1 cores 0-9.

**Note:** If the number of cores specified in the rankfile exceeds the
actual number of cores, `mpirun` will fail with a non-obvious message.
For example, on a machine with 8-cores per socket:

        $ cat 2x10core.rf
        rank 0=host1     slot=0:0-9
        rank 1=host1     slot=1:0-9

        $ mpirun -n 2 -rf 2x10core.rf /bin/true
        [host1:46256] [[20503,0],0] ORTE_ERROR_LOG: Not found in file rmaps_rank_file.c at line 320
        [host1:46256] [[20503,0],0] ORTE_ERROR_LOG: Not found in file base/rmaps_base_map_job.c at line 351
        $

Versus the working:

        $ cat 2x8core.rf
        rank 0=host1     slot=0:0-7
        rank 1=host1     slot=1:0-7

        $ mpirun -n 2 -rf 2x8core.rf /bin/true
        $

The `-report-bindings` flag may be useful for diagnosing problems:

        $ mpirun -report-bindings ......

## DDL options

There are a number of runtime options for the DDL engine. The options are:

`-mode`: This optionally indicates the algorithm and topology. The topology
should match to the rank assignment (e.g. via rankfile) to get the best
performance. If a mode is not provided, it will work as a single ring
configuration (e.g., r:N). Therefore, the total number of MPI clients
(specified as -n N to mpirun) must match with the number of learners in the
topology (specified as -mode in DDL): otherwise, it will show an error like
`invalid dim size=A usr_size=B dim[0]=...`

        b:4x2  => use enhanced NCCL whenever possible (otherwise use ring) for 4x2 configuration

        n:4x2  => use NCCL whenever possible (otherwise use ring) for 4x2 configuration

        r:4x4  => use only RING for 4x4 configuration

        m:4x6  => use only MPI reduce_scatter and all_gatherV for 4x6 configuration (currently disabled)

        c:4x8  => use only RCS for 4x8 configuration

        p:4x16x4 => first activate ex"p"location mode to get the best algorithms for each dimension of 4x16x4

`-dump_iter <N>`: This optionally makes DDL dump network performance on
every N iterations

`-dev_sync <0, 1, or 2>` : This optionally calls cudaDeviceSynchronize
to minimize jitter, default is 0 (which means off). With 1, it
invokes sync once in the beginning of communication. With 2, it invokes
sync twice in the beginning AND end of communication

 `-rebind_iter <N>`: This optionally monitors variation every N
iterations, and performs rebind if a leaner has been slow for the last 3
checks. Too small number will incur variation-check overhead, but too
big number will make training suffer from variation for long time

 `-dbg_level <0,1,2>`: 0 for no, 1 for mild, and 2 for detailed debug
messages

When `dump_iter` is given, you can see the following periodically where
you can find which learner has the maximum jitter and end to end DDL
elapsed time. Also, per dimension, it shows runtime breakdown along with
the selected algorithm for that particular dimension.

![Alt text](ddl_dump.png?raw=true "DDL dump")


**Example of 2 racks of 8 S822LC HPC systems with 4 GPUs on each host**

Generate an appropriate rank file:

        $ python rank_gen.py 4x8x2 host0,host1,host2,??,host15 > 4x8x2.rf

To start IBM Caffe with `mpirun`, specifying rank file and DDL options:

        $ source /opt/DL/caffe/bin/caffe-activate

        $ mpirun -x PATH -x LD_LIBRARY_PATH -n 16 -rf 4x8x2.rf caffe train -solver solver.prototxt -gpu 0 -bvlc -ddl "-mode b:4x8x2 -dump_iter 100"

To start TensorFlow with `mpirun` using custom operator for DDL:

   - Update `train_image_classifier.py` to specify DDL options during
     initialization:

        ddl.Init(4, mode =??mode b:4x8x2 -dump_iter 100??

   - Execute with `mpirun`:

        $ source /opt/DL/ddl-tensorflow/bin/ddl-tensorflow-activate

        $ mpirun -x PATH -x LD_LIBRARY_PATH -x PYTHONPATH -n 16 -rf 4x8.2.rf python train_image_classifier.py ...