2017년 8월 16일 수요일

Minsky 서버를 위한 IBM KVM switch 옵션 소개

Minsky 서버에는 일반 범용 KVM switch를 연결해서 사용하시면 됩니다.  그래서 흔히 IBM의 정품 KVM switch 제품은 없다고 생각하실 수 있으나, 실은 있습니다.  다음의 것들을 주문하시면 됩니다.

7316-TF4  Rack-Mounted Flat Panel Console Kit 1:7316 Model TF4        1                         4269  Cable, USB Conversion Option -- 1.5 meters                         4
    4283  IBM 1x8 Rack Console Switch                                            1  
    8896  USB Travel Keyboard with Cable, Korean                              1  
    9911  Power Cord (4M) Specify -- All (Standard rack power cord)      1      


저 위에 명기된 feature code (FC) 4283의 1x8 Rack Console Switch라는 것은  현재 Lenovo OEM으로 판매되는 LCM2 콘솔 스위치와 크게 다르지 않습니다.   모양은 아래와 같습니다.


(출처 : http://www-01.ibm.com/support/docview.wss?uid=tss1fq120703 )


다만 보시다시피 이 KVM 스위치에는 ARI port가 8개 밖에 없어서 언듯 보면 서버 8대까지만 연결된다고 생각하실 수도 있습니다만, 그렇지는 않습니다.   Daisy-chain 방식으로 연결하면 최대 128대의 서버를 연결할 수 있습니다.

그렇게 daisy-chain 방식으로 연결하기 위해서 필요한 것이 fc 4269 USB Conversion Option cable입니다.  이것은 이렇게 생겼습니다.


(출처 : http://www-01.ibm.com/support/docview.wss?uid=tss1fq120703 )


이건 굳이 daisy-chain 방식으로 하지 않는다고 해도 잘 쓸 수 있는 cable입니다.  위의 그림에서 파란색 RGB port는 당연히 Minsky 서버의 video port에 연결되는 것이고, USB는 Minsky 서버의 USB에 연결되어 keyboard/mouse를 연결하는 것입니다.  그림 오른쪽에 보이는 뭔가 두껍고 port가 2개 나와 있는 것이 KVM switch에 연결되는 port입니다.  Port가 2개 있는 이유는 간단합니다.  1개는 KVM switch와 연결되고, 나머지 하나는 다른 서버와 연결되는 fc 4269 USB Conversion Option cable의 port에 연결되는 것입니다.  아래 그림처럼요.



(출처 : https://lenovopress.com/tips0730 )


보시다시피 이 연결을 위해서는 CAT 5 cable이 필요합니다.  이게 별거 아니고, 그냥 UTP로 된 ethernet cable입니다.  이 CAT 5 cable은 IBM fc 4269에 포함되어 있지 않으므로 일반 UTP cable로 따로 준비해두셔야 합니다.

IBM fc 4269에는 저 USB Conversion Option cable이 4개씩 포함되어 있습니다.  따라서 위의 예처럼 fc 4269의 수량을 4개로 하면 USB Conversion Option cable을 16개 받으시게 됩니다.  그러면 최대 16대의 서버를 저 KVM switch에 연결하실 수 있는 것이지요.   다시 말씀드리지만 그렇게 16대의 서버 연결을 위해서는 fc 4269 외에도 CAT 5 cable (UTP cable)을 32개 별도로 준비하셔야 합니다.


2017년 8월 7일 월요일

POWER9 + Volta의 Cache Coherence : preview

아래 URL은 주로 엔터프라이즈 컴퓨팅 및 수퍼컴 이슈를 다루는 Nextplatform이라는 매체에서 IBM의 Vice President이자 수퍼컴 전문가인 David Turek과 인터뷰한 기사입니다.  David Turek은 저도 실무 차원에서 두어번 teleconference를 한 적이 있는 분인데, 높은 곳에서 호령만 하는 임원이 아니라 진짜 실무 engineer입니다.  이 분은 현재 세계에서 가장 뛰어난 수퍼컴이 될 Sierra와 Summit을 구축하는 CORAL (Collaboration of Oak Ridge, Argonne, and Livermore) 프로젝트를 수행하고 있습니다.



https://www.nextplatform.com/2017/06/21/thinking-cognitive-hpc-nexus-big-blue/

여기서 눈에 띄는 부분은 아래 내용입니다.

TPM: No one else is going to have this coherence between the CPU and the GPU, although AMD might be able to do it now that it is delivering both Epyc processors and Radeon Instinct GPU accelerators. It certain has done some work on coherency in the past with its APUs and that technology could be applied to CPUs and discrete GPUs not on a single die or in a single package. But for now, only Power9 has NVLink and Intel is not offering something that looks like NVLink between its Xeons or Xeon Phis and other types of accelerators. The point is, no one has this yet except IBM and Nvidia.

Dave Turek: Correct. I was down in an exascale conference a couple of weeks ago in Princeton, and Jack Wells, one of the people at Oak Ridge National Laboratories, got up and was talking about progress on the CORAL systems and said that one of the biggest things is going to be this coherency. Forget flops and all of that, the big deal is the coherency. This is a bit under the radar because people are only talking about the technical features of Summit and Sierra machines and they have not realized the impact of this coherence.

이 대화 부분을 요약하면 이렇습니다.

"CORAL 수퍼컴에서는 세계 수퍼컴 최초로 CPU와 GPU간에 coherence가 구현되는데, 진짜 주목할 부분은 (흔히 수퍼컴 성능을 이야기할 때 사용되는) 테라플롭스(TFLOPS) 따위가 아니라 바로 coherence다.  업계 사람들조차도 아직 이 기술의 중요성에 대해 잘 모른다."

이렇게 중요한 기술이라는 coherence라는 것이 대체 무엇일까요 ?   일반적인 cache coherence라는 것은 여러 곳의 메모리/캐쉬에 저장된 data를 일관성있게 유지하는 것을 말합니다.  이는 여러개의 processor를 이용하는 경우 데이터 정합성을 위해 반드시 필요한 기능이며, 대형 SMP 서버의 성능은 CPU와 메모리 등의 성능 외에도, 이 cache coherence를 어떻게 유지하느냐에 달려 있습니다.  아래 그림을 보시면 아마 cache coherence의 개념을 쉽게 이해하실 수 있을 겁니다.  (그림 소스 : https://en.wikipedia.org/wiki/Cache_coherence )


(Cache coherence가 되는 경우)


(Cache coherence가 안 되는 경우)


여기서 Turek이 이야기하는 coherence라는 것은 CPU와 GPU 간의 coherence를 이야기하는 것입니다.  즉, 원래 CPU는 서버 메모리를, GPU는 GPU 메모리를 각각 따로 가지고 있지만, POWER9과 Volta GPU에서는 CPU와 GPU가 그 각각의 메모리를 마치 한덩어리인 것처럼 사용할 수 있게 된다는 것입니다.

기존 GPU 컴퓨팅에서 매우 골치 아팠던 부분은 크게 2가지였습니다.

1) GPU 메모리 크기의 한계  
2) GPU가 연결된 시스템 버스 (PCIe Gen3) 속도의 한계

모든 GPU 컴퓨팅에서, GPU가 뭔가 연산을 하려면 (그것이 전통적 HPC이건 Deep Learning이건) 해당 logic과 data를 CPU 메모리로부터 copy하여 GPU 메모리에 저장부터 해야 했습니다.  그런데 위의 2가지 문제가 항상 그 작업을 어렵게 했습니다.  즉, 보통 12GB ~ 24GB 정도인 GPU 메모리 크기 때문에 원하는 data를 다 집어 넣을 수도 없고, 또 PCIe를 통한 data copy 속도가 충분히 빠르지 않아서 문제였던 것입니다.  Deep learning에 있어서도 이 문제는 마찬가지였습니다.  가령 이미지 데이터를 이용해 신경망을 training시킬 때, 한꺼번에 1024장의 이미지를 처리하면 (batch_size=1024) 좋을텐데 그럴 경우 GPU 메모리가 넘치므로 에러가 났습니다.  또 자율주행이나 자연어 처리 (NLP) 처럼 큰 모델을 사용해야 하는 경우엔 아예 GPU 메모리 상에 모델 자체가 들어가질 않아서 돌릴 수가 없었지요.  그런 경우를 해결하기 위해서 여러장의 GPU에 모델을 쪼개 넣는 model-parallelism을 구현하기도 합니다만, 그런 model-parallelism은 구현하기 쉽지 않은 것이 사실입니다.

이런 상황에서 NVIDIA와 IBM이 내놓은 하드웨어 기술 하나가 많은 것을 바꿔놓습니다.  바로 NVLink입니다.  NVLink는 GPU 연결에 PCIe 대신 사용하는 시스템 버스 기술로서, 특히 IBM은 POWER8 프로세서에 NVLink port를 박아넣음으로써 CPU와 GPU가 40GB/sec X 2 = 80GB/sec로 통신하여 기존 PCIe 대비 2.5배의 대역폭을 낼 수 있도록 만들었습니다.  이는 NVLink가 달린 P100 GPU를 사용하더라도 Intel Xeon 프로세서와 GPU 간은 여전히 PCIe를 써야 하는 x86 서버에서는 불가능한 기술로서, 현재는 IBM Minsky 서버 (S822LC for HPC)에만 구현되어 있습니다.

Minsky 서버의 NVLink 기술은 비단 GPU-GPU, 그리고 CPU-GPU 간의 대역폭을 높였다는 것 외에도 CUDA 8과 Pascal 아키텍처와 함께 근본적인 변화를 가져왔습니다.  바로 Unified Memory입니다.

Unified Memory(UM 혹은 UVM)는 GPU가 서버 RAM을 마치 GPU 메모리인 것처럼 사용할 수 있도록 해주는 기술입니다.  이를 이용하면 GPU 메모리 크기로 인해서 오는 많은 불편함을 해결할 수 있습니다.  그러나 이 UM 기술이 CUDA 6.5부터 지원되었음에도 여태까지 그 활용도는 낮았습니다.  이유는 성능 저하였습니다.  아무래도 DDR3로 되어 있는 서버 메모리가 GDDR5인 GPU 메모리보다는 많이 느렸거든요.  그건 서버 메모리가 DDR4로 바뀌고 GPU 메모리가 HBM2로 바뀐 다음에도 마찬가지입니다.  메모리들이 빨리질 수록 그 연결 기술인 PCIe의 한계가 더 뚜렷이 드러날 뿐이었지요.  그런 상황에서 PCIe를 대체하는 NVLink 기술의 도입은 그 성능 저하 문제를 크게 해결해줄 수 있게 된 것입니다.

여전히 UM 사용은 일반화되어 있지 않습니다.  알고보면 일반 NVLink 1개 link의 대역폭은 40GB/sec로서, PCIe gen3의 32GB/sec보다 엄청나게 빠르다고 할 수도 없습니다.  게다가 대부분의 GPU 서버들이 x86 프로세서를 장착하고 있는데, NVLink P100 GPU를 탑재한 서버라고 해도 x86 프로세서와는 PCIe로 통신해야 합니다.  즉, UM 자체에는 여전히 PCIe를 써야 했습니다.  이에 비해, IBM POWER8 프로세서를 탑재한 Minsky 서버는 POWER8 프로세서와 P100 GPU 간을 NVLink 2개를 aggregation시켜 40+40=80GB/sec로 구현했습니다.  다른 NVLink P100 GPU 서버에 비해 2.5배의 대역폭인 셈이지요.



그렇다면 Minsky에서는 UM을 적극적으로 활용하고 있느냐 ?  예, 실은 이제 막 시작되었습니다.  바로 PowerAI의 Large Model Support (LMS)를 통해서입니다.  이는 바로 최근인 8월 5일 (문서에는 7월 25일이라고 되어 있지만 실제로는 더 늦었습니다) 발표된 새로운 기능으로서, UM 기능을 enable한 IBM 버전의 caffe (caffe-ibm)의 옵션 형태로 구현되었습니다.  이에 대해서는 아래 URL에 자세히 나와 있습니다.

https://developer.ibm.com/linuxonpower/deep-learning-powerai/releases/

그나마 클릭하셔서 자세히 보시기 귀찮으신 분들을 위해 핵심부분만 따오면 아래와 같습니다.

Large Model Support (LMS)

IBM caffe를 사용하실 때, -lms <size in KB> 라는 옵션이 추가되었습니다.  가령 -lms 409600 이라고 해놓으시면 400MB 이상의 메모리 chunk는 CPU에 유지하고, 필요할 때만 GPU 메모리로 가져옵니다.  만약 LMS를 disable하고 싶으시면 -lms 10000000000 처럼 엄청나게 큰 메모리 값을 주시면 됩니다.  또 -lms_frac <0에서 1.0 사이의 값>이라는 옵션을 주셔도 되는데, 가령 -lms_frac 0.4로 준면, 최소 40% 이상의 GPU 메모리를 사용할 때만 LMS 기능이 활성화됩니다.   작은 신경망을 training하실 때는 굳이 LMS를 쓰실 필요가 없으니 이 옵션을 쓰시는 것이 좋습니다.

이 LMS 기능은 이번에는 IBM caffe에만 적용되었습니다만, 점차 더 확대하여 tensorflow에도 적용할 예정입니다.


그런데 이 글 초반에 언급했던 David Turek의 인터뷰 내용을 보면 Cache Coherence라는 것은 POWER8 + Pascal GPU가 아닌, POWER9 + Volta GPU에서 처음으로 도입되는 것처럼 씌여 있습니다.  이건 왜 그럴까요 ?  분명히 Cache Coherence는 Unified Memory보다 더 진전된 기능 같은데, 구체적으로 어떤 점이 더 나아지는 것일까요 ?

이에 대해서는 다음 URL에 꽤 자세히 나와 있습니다.

http://on-demand.gputechconf.com/gtc/2017/presentation/s7285-nikolay-sakharnykh-unified-memory-on-pascal-and-volta.pdf

여기서는 간략하게 표로 정리해보았습니다.




Cache coherence에서 UM으로부터 더 향상된 점을 좀더 쉽게 표현하면 이렇습니다.  Access counter 기능을 이용하여 이젠 access가 잦은 'hot page'들만 GPU로 migration하는 것이 가능해졌고, 또 GPU가 서버 메모리를 access하는 것 뿐만 아니라 CPU도 GPU 메모리를 CPU cache 레벨까지 써서 자유롭게 access하는 것이 가능해진 것입니다.  그 의미는 간단합니다.  GPU 메모리 크기로 인해 제약받았던 기존 GPU 프로그래밍이 상식적인 수준의 성능 저하만 감내한다면 훨씬 더 간편해진다는 것입니다.




적정 수준의 성능 저하만 감내하면 편해진다는데 그 적정 수준이라는 것이 어느 정도이고 또 얼마나 편해지는 것일까요 ?  아직 POWER9 + Volta에 대해서는 자료가 나오지 않았으므로 지금은 POWER8 + Pascal의 UM의 경우를 통해서 짐작해보시지요.

전통적 HPC coding에서도 그렇겠습니다만, deep learning 관점에서도 UM 또는 cache coherence가 주는 혜택은 명백합니다.  한마디로, 과거에는 아예 training을 돌릴 수 없었던 큰 model도 training이 가능해집니다.  기존에는 여러장의 GPU에 걸쳐 복잡한 model-parallelism을 구현해야만 가능했던 training이 훨씬 쉽게 1장의 GPU에서도 가능해지는 것입니다.  또한 과거에는 한번에 (가령) 16장의 이미지만 training 시킬 수 있었다면, 이젠 그 24배인 384장을 한꺼번에 training 시킬 수 있게 됩니다.

다만 이런 편리함은 필연적으로 성능 저하를 불러오는 것이 사실입니다.  아무래도 서버의 DDR4 메모리가 GPU의 HBM2 메모리보다는 훨씬 느린데다, 그걸 PCIe(32GB/sec)가 아닌 NVLink * 2개(80GB/sec)로 연결한다고 해도 메모리 access 속도(115GB/sec)보다는 느리니까요.


(x86 서버에서는 저 붉은색의 PCIe가 병목이 될 수 밖에 없습니다)


아래는 x86 서버에서 NVIDIA Quadro GP100 (Pascal) GPU를 이용해서 Unified Memory로 oversubscription을 한 경우의 deep learning 속도입니다.  그냥 GPU 메모리만 사용할 때에 비해, 속도가 5~9배 느려진 것을 보실 수 있습니다.   이는 x86과 Quadro GP100 사이가 PCIe(32GB/sec)로 연결되었기 때문입니다.



그러나 NVLink *2개(80GB/sec)로 연결된 경우는 어떨까요 ?  NVLink *2개는 PCIe보다 2.5배 더 대역폭이 크기 때문에, 산술적으로는 대략 2~2.5배 정도만 느려질 것으로 예상됩니다.  아직 PowerAI의 LMS를 이용한 deep learning training 성능 테스트 결과가 공식적으로 발표된 것은 없습니다.  그러나 아래 HPC의 일종인 AMR (adaptive mesh refinement technique)에 대해 NVIDIA가 POWER8 + Pascal의 oversubscription 기능을 이용해 테스트한 바를 보면 정말 2배 정도만 느려지는 것을 보실 수 있습니다.   과거에는 아예 돌릴 수 없었던 큰 working set을 POWER8 + Pascal의 oversubscription 기능을 이용하면 1장의 GPU로도 훌륭히 수행할 수 있는 것입니다.



2018년에 CORAL 프로젝트의 두 수퍼컴 클러스터인 Summit과 Sierra가 구축되고나면 cache coherence에 대한 좀더 많은 성능 결과치가 나올 것으로 기대합니다.


Source :
https://devblogs.nvidia.com/parallelforall/beyond-gpu-memory-limits-unified-memory-pascal/
http://on-demand.gputechconf.com/gtc/2016/presentation/s6224-mark-harris.pdf
http://cudacoe.m.gsic.titech.ac.jp/lecture/lecture-wiki/index.php?plugin=attach&refer=hpc2016&openfile=hpc2016_ito.pdf
http://on-demand.gputechconf.com/gtc/2017/presentation/s7285-nikolay-sakharnykh-unified-memory-on-pascal-and-volta.pdf

2017년 8월 2일 수요일

Minsky 서버에서의 jupyter notebook 설치/사용

Minsky 등 ppc64le 환경에서도 당연히 jupyter notebook 사용이 가능합니다.    Miniconda를 이용하여 간단히 설치가 가능한데, Miniconda 자체의 설치에 대해서는 여기를 클릭하세요.

일단 Miniconda가 설치되어 conda를 쓸 수 있게 되면 아래와 같이 하면 됩니다.

mirhenge@powerai:~/miniconda2$ conda install jupyter
Fetching package metadata .........
Solving package specifications: .

Package plan for installation in environment /home/mirhenge/miniconda2:

The following NEW packages will be INSTALLED:

    backports:          1.0-py27_0
    backports_abc:      0.5-py27_0
    bleach:             1.5.0-py27_0
    configparser:       3.5.0-py27_0
    decorator:          4.0.11-py27_0
    entrypoints:        0.2.2-py27_1
    functools32:        3.2.3.2-py27_0
    get_terminal_size:  1.0.0-py27_0
    html5lib:           0.999-py27_0
    ipykernel:          4.6.1-py27_0
    ipython:            5.3.0-py27_0
    ipython_genutils:   0.2.0-py27_0
    ipywidgets:         6.0.0-py27_0
    jinja2:             2.9.6-py27_0
    jsonschema:         2.6.0-py27_0
    jupyter:            1.0.0-py27_3
    jupyter_client:     5.0.1-py27_0
    jupyter_console:    5.1.0-py27_0
    jupyter_core:       4.3.0-py27_0
    libsodium:          1.0.10-0
    markupsafe:         0.23-py27_2
    mistune:            0.7.4-py27_0
    nbconvert:          5.1.1-py27_0
    nbformat:           4.3.0-py27_0
    notebook:           5.0.0-py27_0
    pandocfilters:      1.4.1-py27_0
    path.py:            10.3.1-py27_0
    pathlib2:           2.2.1-py27_0
    pexpect:            4.2.1-py27_0
    pickleshare:        0.7.4-py27_0
    prompt_toolkit:     1.0.14-py27_0
    ptyprocess:         0.5.1-py27_0
    pygments:           2.2.0-py27_0
    python-dateutil:    2.6.0-py27_0
    pyzmq:              16.0.2-py27_0
    scandir:            1.5-py27_0
    simplegeneric:      0.8.1-py27_1
    singledispatch:     3.4.0.3-py27_0
    ssl_match_hostname: 3.4.0.2-py27_1
    terminado:          0.6-py27_0
    testpath:           0.3-py27_0
    tornado:            4.5.1-py27_0
    traitlets:          4.3.2-py27_0
    wcwidth:            0.1.7-py27_0
    widgetsnbextension: 2.0.0-py27_0
    zeromq:             4.1.5-0

The following packages will be UPDATED:

    conda:              4.3.14-py27_0  --> 4.3.21-py27_0

Proceed ([y]/n)? y
...
entrypoints-0. 100% |#########################################| Time: 0:00:00  15.14 MB/s
jupyter_core-4 100% |#########################################| Time: 0:00:00 304.20 kB/s
pickleshare-0. 100% |#########################################| Time: 0:00:00  23.47 MB/s
testpath-0.3-p 100% |#########################################| Time: 0:00:00  35.33 MB/s
tornado-4.5.1- 100% |#########################################| Time: 0:00:00 613.23 kB/s
ipython-5.3.0- 100% |#########################################| Time: 0:00:01 851.46 kB/s
jupyter_client 100% |#########################################| Time: 0:00:00 329.04 kB/s
nbformat-4.3.0 100% |#########################################| Time: 0:00:00 360.09 kB/s
terminado-0.6- 100% |#########################################| Time: 0:00:00  20.31 MB/s
conda-4.3.21-p 100% |#########################################| Time: 0:00:01 503.31 kB/s
ipykernel-4.6. 100% |#########################################| Time: 0:00:00 249.29 kB/s
nbconvert-5.1. 100% |#########################################| Time: 0:00:00 465.37 kB/s
jupyter_consol 100% |#########################################| Time: 0:00:00  39.17 MB/s
notebook-5.0.0 100% |#########################################| Time: 0:00:02   2.07 MB/s
widgetsnbexten 100% |#########################################| Time: 0:00:01 762.68 kB/s
ipywidgets-6.0 100% |#########################################| Time: 0:00:00 167.32 kB/s
jupyter-1.0.0- 100% |#########################################| Time: 0:00:00  11.56 MB/s


이제 다시 login을 하든가 ~/.bashrc를 새로 수행하면 PATH 환경변수가 잡히면서 jupyter 사용이 가능합니다.

mirhenge@powerai:~/miniconda2$ which jupyter
/home/mirhenge/miniconda2/bin/jupyter

Web browser로는 firefox를 사용하시면 됩니다.   여기서는 X 환경을 위해 tightvncviewer를 이용하므로 DISPLAY를 unix:0으로 설정합니다.

mirhenge@powerai:~/miniconda2$ export BROWSER=/usr/bin/firefox
mirhenge@powerai:~/miniconda2$ export DISPLAY=unix:0

이제 jupyter notebook를 구동하면 다음과 같은 메시지가 나옵니다.

mirhenge@powerai:~/miniconda2$ jupyter notebook
[I 13:40:32.487 NotebookApp] Serving notebooks from local directory: /home/mirhenge/miniconda2
[I 13:40:32.488 NotebookApp] 0 active kernels
[I 13:40:32.488 NotebookApp] The Jupyter Notebook is running at: http://localhost:8888/?token=9d13bb86a1a68d0c408740c877045adb2278535287737f5f
[I 13:40:32.488 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 13:40:32.488 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:8888/?token=9d13bb86a1a68d0c408740c877045adb2278535287737f5f
[I 13:40:35.286 NotebookApp] Accepting one-time-token-authenticated connection from 127.0.0.1

이때 tightvncserver를 설정해 놓았다면 그 화면을 통해 vncviewer에서 browser가 열립니다.




이후로는 vncviewer를 통하지 않고도 remote에서도 web browser를 이용해 접속할 수 있도록 아래와 같이 설정 파일을 생성하고 이어서 수정해줍니다.

mirhenge@powerai:~$ jupyter notebook --generate-config

mirhenge@powerai:~$ vi /home/mirhenge/.jupyter/jupyter_notebook_config.py
...
## The IP address the notebook server will listen on.
#c.NotebookApp.ip = 'localhost'
c.NotebookApp.ip = '*'


이제 다시 start해주면 굳이 local server에서 띄운 browser 뿐만 아니라 외부 PC에서 띄운 browser로도 접속이 가능합니다.

mirhenge@powerai:~$ jupyter notebook
[W 13:57:17.150 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 13:57:17.158 NotebookApp] Serving notebooks from local directory: /home/mirhenge
[I 13:57:17.158 NotebookApp] 0 active kernels
[I 13:57:17.158 NotebookApp] The Jupyter Notebook is running at: http://[all ip addresses on your system]:8888/?token=acde16ba9020fd7cc174d894f760772af7257c619579519d
[I 13:57:17.158 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 13:57:17.159 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:8888/?token=acde16ba9020fd7cc174d894f760772af7257c619579519d
[I 13:57:18.245 NotebookApp] Accepting one-time-token-authenticated connection from 127.0.0.1
[I 13:57:32.011 NotebookApp] 302 GET / (211.109.178.33) 0.33ms
[I 13:57:32.165 NotebookApp] 302 GET /tree? (211.109.178.33) 0.54ms


이때 처음 접속할 때는 위에 display된 token(위의 붉은색 부분)을 copy & paste 해서 넣어야 합니다.







한번 테스트를 해봅니다.   다음과 같이 IBM lab service에서 만든 demo용 English --> French 번역 deep learning용 package를 다운로드 받습니다.

mirhenge@powerai:~$ git clone https://github.com/ppradhan01/dl-language-translation.git
Cloning into 'dl-language-translation'...
remote: Counting objects: 13, done.
remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 13
Unpacking objects: 100% (13/13), done.
Checking connectivity... done.


mirhenge@powerai:~$ cd dl-language-translation/
mirhenge@powerai:~/dl-language-translation$ ls
code.ipynb  data  helper.py  problem_unittests.py  README.md

mirhenge@powerai:~/dl-language-translation$ cd data

mirhenge@powerai:~/dl-language-translation/data$ ls
small_vocab_en  small_vocab_fr

mirhenge@powerai:~/dl-language-translation/data$ head small_vocab_en
new jersey is sometimes quiet during autumn , and it is snowy in april .
the united states is usually chilly during july , and it is usually freezing in november .
california is usually quiet during march , and it is usually hot in june .
the united states is sometimes mild during june , and it is cold in september .
your least liked fruit is the grape , but my least liked is the apple .
his favorite fruit is the orange , but my favorite is the grape .
paris is relaxing during december , but it is usually chilly in july .
new jersey is busy during spring , and it is never hot in march .
our least liked fruit is the lemon , but my least liked is the grape .
the united states is sometimes busy during january , and it is sometimes warm in november .

mirhenge@powerai:~/dl-language-translation/data$ head small_vocab_fr
new jersey est parfois calme pendant l' automne , et il est neigeux en avril .
les états-unis est généralement froid en juillet , et il gèle habituellement en novembre .
california est généralement calme en mars , et il est généralement chaud en juin .
les états-unis est parfois légère en juin , et il fait froid en septembre .
votre moins aimé fruit est le raisin , mais mon moins aimé est la pomme .
son fruit préféré est l'orange , mais mon préféré est le raisin .
paris est relaxant en décembre , mais il est généralement froid en juillet .
new jersey est occupé au printemps , et il est jamais chaude en mars .
notre fruit est moins aimé le citron , mais mon moins aimé est le raisin .
les états-unis est parfois occupé en janvier , et il est parfois chaud en novembre .


이제 browser의 notebook에서, 위에서 download 받은 dl-language-translation directory로 찾아내려가 code.ipynb를 클릭하면 됩니다.




그러면 이제 notebook이 열리고, 필요시 위쪽 메뉴 bar에 있는 play 버튼 (player의 삼각형 버튼)을 눌러서 해당 code 부분을 직접 수행해볼 수도 있습니다.







왜 GPU를 이용한 deep learning에 LSF job scheduler가 필요한가 ?


IBM LSF(Load Sharing Facility)는 한마디로 queue 방식의 job scheduler로서, 주로 수퍼컴 클러스터에서 사용되는 SW 제품입니다.  그에 비해 deep learning training 업무는 GPU를 몇 장 장착한 1대의 서버 또는 desktop workstation에서 수행하는 것이 보통이므로 LSF와는 잘 어울리지 않는다고 생각하기 쉽습니다.

그러나 그렇지 않습니다.  GPU를 사용하는 deep learning training 업무야말로 LSF를 이용할 때 볼 수 있는 혜택이 무척 큰 업무입니다.  이유는 무엇보다 GPU 자원이 비싼 것에 비해 정작 활용률이 낮기 때문입니다.

전통적으로 deep learning training은 연구원들이 개인별로 혹은 팀별로 구매한 서버나 워크스테이션 1~2대에서 수행해왔습니다.  최근의 AI 붐으로 인해 점점 더 많은 연구원들이 더 많은 training을 하게 되었고, 이로 인해 기업이나 연구소에서는 연구원들의 요청에 따라 더 많은 서버를 구매하게 되었습니다.

또한 전통적으로 팀별로 독립된 연구 환경을 선호하는 연구하는 연구원들의 특성상, 다른 팀 또는 다른 연구원들과 하나의 GPU 서버를 공유하여 training을 수행하는 일이 많지는 않았습니다.  더 좋은 GPU 서버 1대를 사서 공유하는 것 보다는 좀더 낮은 사양의 GPU 서버를 2대 사서 각각 1대씩 따로 운용하는 것을 선호했지요.

문제는 그러다보니 GPU 서버들의 구매 비용은 말할 것도 없고, 전기사용량이 많고 소음과 발열량도 많은 GPU 서버들의 관리에 문제가 생기기 시작했다는 점입니다.  더 큰 문제는 그렇게 많은 비용을 들여 사들인 GPU 서버들의 활용률이 생각보다 매우 낮다는 것입니다.

아무리 열심히 연구활동을 하는 연구원들이라고 해도, 24시간 계속 끊이지 않고 뭔가 모델을 training시키지는 않습니다.  새로운 연구 논문도 읽어야 하고, data labeling 작업도 감독해야 할테니까요.  그러다보니 A 연구실에서는 GPU 자원이 부족하여 GPU 서버를 사야하는데 바로 옆 방의 B 연구실에서는 GPU 서버가 그냥 놀면서 전기만 먹어대는 경우도 많습니다.  더 최악인 것은, 바로 다음 달이 되면 A 연구실의 GPU가 놀 때 B 연구실에서는 GPU가 부족하다고 GPU 서버를 1대 더 사달라고 하는 경우지이요.

이런 문제를 해결해주는 것이 IBM LSF입니다.  LSF는 GPU 자원을 모니터링하며 관리하다가, 연구원들이 queue에 submit하는 training job들을 최적의 GPU에 알아서 배치해 줍니다.  그럼으로써 전체적인 GPU 자원이 낭비되는 일 없이 활용률을 높여주고, 또 연구원들도 훨씬 편리하게 작업을 수행할 수 있게 해줍니다.

아래의 두가지 시나리오를 보시면 쉽게 이해가 되실 것입니다.

#1.  당장 급하게 2개의 GPU를 이용하여 돌려야 하는 training이 있습니다.  그런데 이미 김박사님이 4개의 GPU를, 이박사님이 3개의 GPU를 이용하여 뭔가를 돌리고 계십니다.  두분께 전화를 걸어 여쭤보니 김박사님은 몇시에 끝날지 잘 모르겠다고 하시고, 이박사님은 아마 새벽 2시쯤 끝날 것이라고 말씀하십니다.   과연 저는 새벽 2시까지 기다렸다가 이박사님의 job이 끝나는 것을 확인한 뒤에 제 training 작업을 걸어놓고 퇴근해야 하나요 ?




#2.  연구원 A는 GPU 4장이 장착된 서버가 현재 놀고 있는 것을 확인했습니다.  그래서 GPU 2장, 즉 gpu0과 gpu1을 이용하여 caffe training을 하나 돌렸습니다.  그런데, 하필 거의 동시에 연구원 B도 이 서버가 놀고 있다고 생각하고는 역시 gpu0과 gpu1을 이용하여 caffe training을 걸었습니다.   과연 어떻게 될까요 ?  매번 누가 이 서버에서 몇번 GPU를 이용해서 돌리려는지 확인한 뒤 job script를 수정해야 할까요 ?  




결론적으로, 약간의 비용을 들여서 LSF를 구축하시는 것이 무작정 GPU 서버를 더 사는 것보다 훨씬 더 효율이 높으며, 이는 비단 GPU 서버 HW 구매 비용 뿐만 아니라 상면과 전력소비량, 공조기 비용 등의 절감에 큰 도움이 됩니다.   또한, 현업 연구원들 입장에서도 훨씬 더 편리하고 마음 편하게 연구 활동에만 전념할 수 있다는 장점이 있습니다.  

LSF를 이용한 deep learning의 가장 분명한 reference는 바로 IBM 자신의 Poughkeepsie 벤치마크 센터의 수퍼컴입니다.   거기서의 LSF 사용이 얼마나 간단한지는 여기서 확인하시기 바랍니다.

IBM Poughkeepsie 벤치마크 센터에서의 LSF를 이용한 deep learning training 수행

이번 posting에서는 IBM Poughkeepsie (POK) 벤치마크 센터를 이용하여 Minsky 서버를 이용한 deep learning 성능 벤치마크 테스트를 수행하는 방법에 대해 알아보겠습니다.  단, 여기서의 주 내용은 POK 센터 수퍼컴 클러스터의 개략적인 GPFS 및 LSF 환경 및 그 사용방법에 대한 가이드일 뿐이고, 이 수퍼컴을 사용하기 위한 신청/승인 절차는 다루지 않습니다.   이 수퍼컴 클러스터는 IBM HW/SW의 구매를 고려 중이신 고객분들의 capacity sizing 등 각종 PoC와 performance benchmark test를 위해 사용됩니다.

먼저, IBM 영업측을 통해서 POK 벤치마크 센터의 사용 승인을 받으시면 VPN 연결 방법 및 관련 id/passwd를 받게 됩니다.

VPN 연결 뒤에 연결하셔야 하는 서버는 실제로 고객분이 성능 테스트를 수행하실 서버가 아니라, login 서버라고 하는 서버입니다.  POK 벤치마크 센터의 수퍼컴은 수십대의 POWER8 서버로 되어 있는데, 고객분들은 이 서버들 중 하나를 할당 받는 형태로 테스트를 하는 것이 아니라 이 서버들의 computing power를 LSF라고 하는 job scheduler를 통해 할당받는 것입니다.  고객분들이 접속하시는 이 login 서버는 job scheduler의 master 서버 역할을 하며, 여기서는 다음과 같은 것을 수행하실 수 있습니다.

- 수행하려는 application과 data의 컴파일 및 설치
- 수행를 위해 필요한 shell script 등의 작성과 간단한 동작 여부 테스트

간혹 이 login 서버에서 아예 성능 테스트를 돌려버리시는 분들이 있는데, 그럴 경우 제대로 된 성능을 얻기 어려울 뿐만 아니라 이 수퍼컴을 이용하시는 전세계의 많은 다른 고객분들께도 폐를 끼치는 행위가 되므로 절대 그러지 마시기를 부탁드립니다.  많은 수퍼컴 클러스터에서는 그런 일을 막기 위해 login 서버의 사양을 일부러 작은 것으로 하거나 GPU가 달려 있지 않은 것으로 구성하기도 합니다.

이 login 서버와 수퍼컴 노드들은 모두 Spetrum Scale (옛이름 GPFS)라는 병렬파일시스템으로 묶여있습니다.  즉, 어느 서버에 login하더라도 (내장 disk를 이용한 일부 파일시스템을 제외하고는) 모두 같은 파일시스템이 마운트 되어 있는 것을 보실 수 있으며, login 서버에서 저장해 놓은 파일들은 수퍼컴 내의 어느 서버에서도 다 read/write할 수 있습니다.  물론 각 user id도 login 서버와 수퍼컴 노드들에서 다 동일하게 만들어져 있고, user의 홈 디렉토리도 이 GPFS 파일시스템으로 되어 있으므로 login 서버의 홈 디렉토리에 저장된 내용은 어느 노드에서라도 다 동일하게 보실 수 있습니다.

Login 서버에 접속하시면 다음과 같이 여러 filesystem들이 마운트 되어 있는 것을 보실 수 있습니다.  그 중 앞이 /gpfs 로 시작되는 파일시스템들이 Spectrum Scale (GPFS) 파일시스템들입니다.  고객분은 시스템 userid/passwd를 받으실 때 어느 특정 GPFS 파일시스템을 사용하라는 가이드를 받으실 것입니다.  대부분의 경우, /gpfs/gpfs_gl4_16mb를 사용하라는 가이드를 받으실 것이고, 또 홈 디렉토리가 이미 그 파일시스템으로 잡혀 있을 것입니다.

b7p193aa@p10login1:~$ pwd
/gpfs/gpfs_gl4_16mb/home/b7p193aa

b7p193aa@p10login1:~$ df -h
Filesystem                                    Size  Used Avail Use% Mounted on
udev                                          243G     0  243G   0% /dev
tmpfs                                          52G  778M   51G   2% /run
/dev/sda2                                     879G   42G  793G   5% /
tmpfs                                         256G   17M  256G   1% /dev/shm
tmpfs                                         5.0M     0  5.0M   0% /run/lock
tmpfs                                         256G     0  256G   0% /sys/fs/cgroup
cgmfs                                         128K     0  128K   0% /run/cgmanager/fs
fserv3.pbm.ihost.com:/export/ibmplatform       98G   38G   61G  39% /vol/ibmplatform
tmpfs                                          52G     0   52G   0% /run/user/0
gpfs_gl4_16mb_bench                           221T  123T   98T  56% /gpfs/gpfs_gl4_16mb_bench
gpfs_gl4_8mb                                   75T   23T   53T  30% /gpfs/gpfs_gl4_8mb
gpfs_gs2_512k                                 2.1T  1.9T  130G  94% /gpfs/gpfs_gs2_512k
gpfs_stage1                                    66T   57T  8.7T  87% /gpfs/gpfs_stage1
gpfs_2gl4_8mb                                  61T  8.6T   52T  15% /gpfs/gpfs_2gl4_8mb
gpfs_gl4_16mb                                 165T  126T   39T  77% /gpfs/gpfs_gl4_16mb
/dev/nvme0n1p1                                2.9T  332M  2.8T   1% /nvme3T
....

이 수퍼컴 클러스터 내의 노드들의 사양과 OS 등은 용도/그룹별로 서로 약간 다릅니다.  일부는 전통적 HPC 테스트를 위해 Redhat OS가 설치되어 있고, 일부는 deep learning을 위해 Ubuntu 16.04와 함께 IBM PowerAI toolkit이 설치되어 있습니다.  그 중 어느 쪽에 login 해야 하느냐는 고민하실 필요가 없습니다.  왜냐하면 해당 노드들에는 직접 login 하실 일이 없고, login 노드에서의 LSF job submit 형태로만 이용을 하시게 되거든요.  이제 그 과정을 찬찬히 보시겠습니다.

LSF는 job scheduler SW이고, 이를 이용하시려면 몇가지의 간단한 명령어만 익히시면 사용 가능하십니다.  특히 1대의 노드만을 이용하여 deep learning을 하시는 분들께서는 매우 간단한 명령 몇개만 아시면 됩니다.

bqueues : job을 submit할 큐의 정보를 보여줍니다
bsub : job을 큐에 submit 해줍니다
bjobs : 큐에 submit된 job의 상태를 보여줍니다
bhist : 현재 수행 중인, 혹은 이미 수행이 끝난 job의 history를 보여줍니다
bkill : submit되어 현재 수행 중인 상태의 job을 도중에 kill 시켜 줍니다
bhosts : 수퍼컴 클러스터 내의 노드들 상황을 보여줍니다.

이제 자세히 보시겠습니다.  이 수퍼컴 클러스터에서 job을 submit할 queue에 어떤 것들이 있는지 bqueues 명령을 통해 보실 수 있습니다.

b7p193aa@p10login1:~$ bqueues
QUEUE_NAME      PRIO STATUS          MAX JL/U JL/P JL/H NJOBS  PEND   RUN  SUSP
test-stream      30  Open:Inact        -    -    -    -     0     0     0     0
s822lc_p100_k80  30  Open:Active       -    -    -    -  8616  6568  2048     0
822normal        30  Open:Inact        -    -    -    -     0     0     0     0
s822lc_p100      30  Open:Active       -    -    -    -     3     0     3     0
s822lc_p100nvme  30  Open:Active       -    -    -    -   151     0   151     0
normal           30  Open:Active       -    -    -    -     0     0     0     0
s822lc_k80       30  Closed:Inact      -    -    -    -     0     0     0     0

Deep learning을 하실 고객분들은 이 중 s822lc_p100nvme 이라는 이름의 queue에 job을 submit 하셔야 합니다.   전통적 HPC를 하실 분들은 s822lc_p100 라는 queue를 이용하셔야 합니다.

수행할 job을 위한 shell script를 미리 만들어 두시는 것이 편합니다.  여기서는 PowerAI에 포함된 tensorflow를 이용하여 CIFAR-10 training 하는 shell script를 준비했습니다.  현재의 shell에서 수행되는 것이 아니라 동일 GPFS 파일시스템을 마운트하고 있는 다른 서버에서 LSF를 통해서 수행되는 것이므로, 가급적 모든 path는 절대 path로 써주시는 것이 좋습니다.

b7p193aa@p10login1:~$ cat cifar10.sh
#!/bin/bash
source /opt/DL/tensorflow/bin/tensorflow-activate
source /opt/DL/bazel/bin/bazel-activate
export FLOWERS_DIR=/gpfs/gpfs_gl4_16mb/b7p193aa/inception/models/inception
export INCEPTION_DIR=/gpfs/gpfs_gl4_16mb/b7p193aa/inception
/gpfs/gpfs_gl4_16mb/b7p193aa/inception/models/inception/bazel-bin/inception/flowers_train --train_dir=$FLOWERS_DIR/train --data_dir=$FLOWERS_DIR/data --pretrained_model_checkpoint_path=$INCEPTION_DIR/inception-v3/model.ckpt-157585 --fine_tune=True --initial_learning_rate=0.005 -input_queue_memory_factor=1 --max_steps=500 --num_gpus 4 --batch_size=64

이제 이 cifar10.sh를 LSF의 s822lc_p100nvme 이라는 이름의 queue에 submit 하겠습니다.

b7p193aa@p10login1:~$ bsub -q s822lc_p100nvme /gpfs/gpfs_gl4_16mb/home/b7p193aa/cifar10.sh
Job <113856> is submitted to queue <s822lc_p100nvme>.

Job ID 113856를 이용하여 현재 상황이 어떤지 등을 보실 수 있습니다.  먼저 job 상황을 보기 위해 bjobs 명령을 써보겠습니다.

b7p193aa@p10login1:~$ bjobs 113856
JOBID   USER    STAT  QUEUE      FROM_HOST   EXEC_HOST   JOB_NAME   SUBMIT_TIME
113856  b7p193a RUN   s822lc_p10 p10login1    p10a106     *ifar10.sh Aug  2 00:38

현재 run 중이고, p10a106이라는 서버에서 수행 중임을 알 수 있습니다.  

bhist 명령으로 보시면 이제 막 job이 할당되어 해당 노드상에서 pid 142480로 시작된 것을 보실 수 있습니다.

b7p193aa@p10login1:~$ bhist -l 113856

Job <113856>, User <b7p193aa>, Project <default>, Command </gpfs/gpfs_gl4_16mb/
                     home/b7p193aa/cifar10.sh>
Wed Aug  2 00:38:06: Submitted from host <p10login1>, to Queue <s822lc_p100nvme
                     >, CWD <$HOME>;
Wed Aug  2 00:38:07: Dispatched 1 Task(s) on Host(s) <p10a106>, Allocated 1 Slo
                     t(s) on Host(s) <p10a106>, Effective RES_REQ <select[type
                     == local] order[r15s:pg] >;
Wed Aug  2 00:38:08: Starting (Pid 142480);

Summary of time in seconds spent in various states by  Wed Aug  2 00:38:08
  PEND     PSUSP    RUN      USUSP    SSUSP    UNKWN    TOTAL
  1        0        1        0        0        0        2

이어서 bhosts 명령으로 확인하시면, 이 p10a106 노드에서 뭔가 한창 돌아가고 있는 것을 보실 수 있습니다.

b7p193aa@p10login1:~$ bhosts p10a106
HOST_NAME          STATUS       JL/U    MAX  NJOBS    RUN  SSUSP  USUSP    RSV
p10a106           ok              -    160    150    150      0      0      0

Job이 돌아가는 모습을 보시려면 bpeek 명령을 쓰실 수 있습니다.  원래 console 상에 display 되어야 하는 message 등을 여기서 엿볼 수 있습니다.

b7p193aa@p10login1:~$ bpeek 113856
<< output from stdout >>

<< output from stderr >>
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally


시간이 지난 뒤 다시 bhist 명령을 내려보면 이제 완료된 것을 보실 수 있습니다.

b7p193aa@p10login1:~$ bhist -l 113856

Job <113856>, User <b7p193aa>, Project <default>, Command </gpfs/gpfs_gl4_16mb/
                     home/b7p193aa/cifar10.sh>
Wed Aug  2 00:38:06: Submitted from host <p10login1>, to Queue <s822lc_p100nvme
                     >, CWD <$HOME>, Error File <./err.2>;
Wed Aug  2 00:38:07: Dispatched 1 Task(s) on Host(s) <p10a106>, Allocated 1 Slo
                     t(s) on Host(s) <p10a106>, Effective RES_REQ <select[type
                     == local] order[r15s:pg] >;
Wed Aug  2 00:38:08: Starting (Pid 142480);
Wed Aug  2 00:38:14: Running with execution home </gpfs/gpfs_gl4_16mb/home/b7p1
                     93aa>, Execution CWD </gpfs/gpfs_gl4_16mb/home/b7p193aa>,
                     Execution Pid <142480>;
Wed Aug  2 02:14:55: Done successfully. The CPU time used is 692931.6 seconds;
Wed Aug  2 02:15:00: Post job process done successfully;

MEMORY USAGE:
MAX MEM: 20.7 Gbytes;  AVG MEM: 16.2 Gbytes

Summary of time in seconds spent in various states by  Wed Aug  2 02:15:00
  PEND     PSUSP    RUN      USUSP    SSUSP    UNKWN    TOTAL
  1        0        5808     0        0        0        5809

그 결과물로 나오는 model file은 미리 정해진 위치인 $INCEPTION/models/inception/train 밑에 아래와 같이 생성된 것을 확인하실 수 있습니다.

b7p193aa@p10login1:~$ ls /gpfs/gpfs_gl4_16mb/b7p193aa/inception/models/inception/train
checkpoint  model.ckpt-0.data-00000-of-00001  model.ckpt-0.index  model.ckpt-0.meta



가끔은 작성한 shell이 제대로 수행되지 않고 error가 나는 경우가 있습니다.  이때 error 메시지를 봐야 수정을 할텐데, 제가 위에 정리한 내용에는 그 부분이 없지요.  이는 bsub 명령을 내릴 때 -e 옵션을 주시면 됩니다.

아래처럼 -e 뒤에 경로를 포함한 파일명을 주시면 그 파일에 error 메시지가 쌓입니다.

b7p193aa@p10login1:~$ bsub -q s822lc_p100nvme -e ./err.1 /gpfs/gpfs_gl4_16mb/home/b7p193aa/cifar10.sh
Job <113855> is submitted to queue <s822lc_p100nvme>.

이 job은 아래와 같이 exit code 127을 내면서 시작하자마자 죽은 것을 보실 수 있습니다.

b7p193aa@p10login1:~$ bhist -l 113855

Job <113855>, User <b7p193aa>, Project <default>, Command </gpfs/gpfs_gl4_16mb/
                     home/b7p193aa/cifar10.sh>
Wed Aug  2 00:36:20: Submitted from host <p10login1>, to Queue <s822lc_p100nvme
                     >, CWD <$HOME>, Error File <./err.1>;
Wed Aug  2 00:36:21: Dispatched 1 Task(s) on Host(s) <p10a119>, Allocated 1 Slo
                     t(s) on Host(s) <p10a119>, Effective RES_REQ <select[type
                     == local] order[r15s:pg] >;
Wed Aug  2 00:36:22: Starting (Pid 96410);
Wed Aug  2 00:36:28: Running with execution home </gpfs/gpfs_gl4_16mb/home/b7p1
                     93aa>, Execution CWD </gpfs/gpfs_gl4_16mb/home/b7p193aa>,
                     Execution Pid <96410>;
Wed Aug  2 00:36:28: Exited with exit code 127. The CPU time used is 0.4 seconds;
Wed Aug  2 00:36:28: Completed <exit>;

Summary of time in seconds spent in various states by  Wed Aug  2 00:36:28
  PEND     PSUSP    RUN      USUSP    SSUSP    UNKWN    TOTAL
  1        0        7        0        0        0        8

./err.1 파일을 열어보면 아래와 같이 제가 경로명을 잘못 줬기 때문에 발생한 것임을 아실 수 있습니다.

b7p193aa@p10login1:~$ cat ./err.1
/gpfs/gpfs_gl4_16mb/home/b7p193aa/cifar10.sh: line 6: /gpfs_gl4_16mb/b7p193aa/inception/models/inception/bazel-bin/inception/flowers_train: No such file or directory

2017년 7월 24일 월요일

Minsky 서버에서 NVIDIA DIGITS로 MNIST LeNet training 해보기

이번 posting 내용은 아래 URL에 있는 것을 IBM ppc64le 아키텍처 위에서 제공되는 IBM PowerAI tool에 포함된 digits로 구현해본 것입니다.

https://github.com/NVIDIA/DIGITS/blob/master/docs/GettingStarted.md

DIGITS는 NVIDIA에서 내놓은 오픈소스 기반의 딥 러닝 toolkit입니다.  DIGITS에 대해서 NVIDIA가 소개해놓은 글 ( https://developer.nvidia.com/digits )을 읽어보면 대체 이게 뭐라는 것인지 명확하지는 않습니다.  아래와 같이 그냥 좋은 말만 나와 있거든요.  사실 이런 경향은 IBM을 포함한 모든 벤더들이 다 보여주고 있습니다.

The NVIDIA Deep Learning GPU Training System (DIGITS) puts the power of deep learning into the hands of engineers and data scientists. DIGITS can be used to rapidly train the highly accurate deep neural network (DNNs) for image classification, segmentation and object detection tasks.

결론적으로, DIGITS는 caffe나 torch와 같은 딥 러닝용 framework 자체는 아니고, caffe나 torch를 이용하여 딥 러닝을 수행할 때 좀 더 쉽고 빠르게 할 수 있게 해주는 유용한 웹 기반의 tool입니다.  아마 당장 나오는 질문이 '그럼 tensorflow는 안 되나요'라는 것일텐데, 2017년 7월 24일 현재 당장은 안 됩니다.  그러나 NVIDIA도 DIGITS에 tensorflow 지원을 추가하려고 하고 있고, 이번 달에 나온다고 합니다.

여기서는 caffe를 이용해서 가장 간단한 딥 러닝 training인 MNIST dataset에 대한 LeNet 신경망 training을 DIGITS를 통해 해보도록 하겠습니다.

nvidia-caffe와 digits의 설치는 IBM PowerAI toolkit에서 제공되는 것을 그대로 사용하겠습니다.   그 설치 방법에 대해서는 지난번 posting ( http://hwengineer.blogspot.kr/2017/05/minsky-cuda-powerai-tuning.html )을 참조하시기 바랍니다.

먼저, 그냥 caffe를 이용하여 MNIST dataset에 대한 LeNet 신경망 training을 하는 방법을 봐야 하는데, 이에 대해서는 역시 지난번 posting ( http://hwengineer.blogspot.kr/2017/06/mnist-gpu-cpu-deep-learning.html )에 잘 나와 있으니 그를 참조하시기 바랍니다.

오늘 사례는 GPU가 없는 환경에서 digits를 수행해야 합니다.

시작하기 전에 DIGITS 관련 환경변수를 setup해야 합니다.  DIGITS는 위에서 언급한 것처럼 caffe와 torch를 사용하는 것이기 때문에, "apt-get install digits" 명령만 내려도 자동으로 caffe-nv와 torch를 함께 설치합니다.  마찬가지로, DIGITS를 사용하기 위해 PowerAI에서 제공하는 환경변수 setup script인 /opt/DL/digits/bin/digits-activate를 수행해도, caffe-nv와 torch 관련 환경변수들이 함께 setup 됩니다.   먼저 기본 상태의 PATH 관련 환경 변수를 확인하겠습니다.

u0017496@sys-88165:~$ env | grep PATH
PATH=/home/u0017496/bin:/home/u0017496/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

이제 PowerAI에서 제공하는 환경변수 setup script인 /opt/DL/digits/bin/digits-activate를 수행하겠습니다.  앞에 sh 또는 .을 함께 입력하셔야 한다는 것에 유의하십시요.

u0017496@sys-88165:~$ . /opt/DL/digits/bin/digits-activate

이제 다시 환경변수를 확인하면, openblas와 nccl, cuda 등은 물론이고, torch를 위한 LUA 관련 PATH들도 자동 설정된 것을 보실 수 있습니다.

u0017496@sys-88165:~$ env | grep PATH
LD_LIBRARY_PATH=/opt/DL/openblas/lib:/opt/DL/nccl/lib:/opt/DL/caffe-nv/lib:/usr/local/cuda-8.0/lib64:/opt/DL/torch/lib:/opt/DL/torch/lib/lua/5.1
PATH=/home/u0017496/bin:/home/u0017496/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/DL/caffe-nv/bin:/opt/DL/torch/bin:/opt/DL/digits
LUA_PATH=/home/u0017496/.luarocks/share/lua/5.1/?.lua;/home/u0017496/.luarocks/share/lua/5.1/?/init.lua;/opt/DL/torch/share/lua/5.1/?.lua;/opt/DL/torch/share/lua/5.1/?/init.lua;
LUA_CPATH=/home/u0017496/.luarocks/lib/lua/5.1/?.so;/opt/DL/torch/lib/lua/5.1/?.so;
DYLD_LIBRARY_PATH=/opt/DL/torch/lib:/opt/DL/torch/lib/lua/5.1
PYTHONPATH=/opt/DL/caffe-nv/python:/opt/DL/digits

이제 python을 이용하여 홈 디렉토리 밑에 mnist dataset을 download 받겠습니다.  이때 digits의 download_data 모듈을 유용하게 사용할 수 있습니다.

u0017496@sys-88165:~$ python -m digits.download_data mnist ~/mnist
Downloading url=http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz ...
Downloading url=http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz ...
Downloading url=http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz ...
Downloading url=http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz ...
Uncompressing file=train-images-idx3-ubyte.gz ...
Uncompressing file=train-labels-idx1-ubyte.gz ...
Uncompressing file=t10k-images-idx3-ubyte.gz ...
Uncompressing file=t10k-labels-idx1-ubyte.gz ...
Reading labels from /home/u0017496/mnist/train-labels.bin ...
Reading images from /home/u0017496/mnist/train-images.bin ...
Reading labels from /home/u0017496/mnist/test-labels.bin ...
Reading images from /home/u0017496/mnist/test-images.bin ...
Dataset directory is created successfully at '/home/u0017496/mnist'
Done after 24.0719361305 seconds.

이 python 명령 한줄이면 mnist dataset이 모두 자동으로 다 준비된 것입니다.  아래에서 보시다시피 ~/mnist/train 밑에 train data가 들어가 있는 것을 확인하실 수 있습니다.  ~/mnist/test 밑의 data는 이름과는 달리 test가 아니라 validation을 위해 사용될 것입니다.

u0017496@sys-88165:~$ cd mnist

u0017496@sys-88165:~/mnist$ pwd
/home/u0017496/mnist

u0017496@sys-88165:~/mnist$ ls
t10k-images-idx3-ubyte.gz  test-images.bin  train-images.bin            train-labels-idx1-ubyte.gz
t10k-labels-idx1-ubyte.gz  test-labels.bin  train-images-idx3-ubyte.gz
test                       train            train-labels.bin

u0017496@sys-88165:~/mnist$ cd train

u0017496@sys-88165:~/mnist/train$ ls
0  1  2  3  4  5  6  7  8  9  labels.txt  train.txt


이제 digits-server를 수행합니다.  이는 shell cript로 되어 있으며, daemon화 되어 있지는 않으므로 nohup ~ & 구문을 써서 구동하시는 것이 좋습니다.

u0017496@sys-88165:~$ /opt/DL/digits/digits-devserver &
  ___ ___ ___ ___ _____ ___
 |   \_ _/ __|_ _|_   _/ __|
 | |) | | (_ || |  | | \__ \
 |___/___\___|___| |_| |___/ 5.0.0

/usr/lib/python2.7/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
NVIDIA: no NVIDIA devices found
cudaRuntimeGetVersion() failed with error #30
2017-07-20 02:33:14 [INFO ] Loaded 0 jobs.

위에서 보시다시피 no NVIDIA devices found라고 error/warning이 있습니다만, 이는 제가 쓰는 이 시스템에 GPU가 없어서 그런 것이니 일단 무시하십시요.


이제 웹 브라우저를 이용해서 http://서버IP주소:5000/ 로 접속하십시요.   그러면 아래와 같이 'Models'라는 메뉴 화면에 접속하게 됩니다.



여기서 아래와 같이 Datasets > New Dataset > Images > Classification 순으로 click 하면 login page로 접속하게 됩니다.  사용자 id를 넣으라고 나올텐데, 이는 사용하시는 OS user명을 넣으시면 됩니다.



이제 아래와 같은 New Image Classification Dataset 메뉴에 들어왔습니다.  여기서 training images에는 위에서 보신 mnist train 디렉토리를 아래와 같이 입력하시고, image type은 Grayscale로, image size는 28 x 28로 변경하십시요.  또 'separate images validation folder'에 check 표시를 클릭해주시면 추가 메뉴가 나오는데, 거기에는 위에서 언급한 대로 mnist test 디렉토리를 입력해주시면 됩니다.  Backend DB는 LMDB로 지정하시고, dataset 이름을 적절한 것으로 지정하십시요.  여기서는 MNIST로 정하겠습니다.




그리고나서 이제 바닥의 파란색 Create 버튼을 click하시면 됩니다.  그러면 우측 상태 메뉴에서 Job Status가 running으로 나오는 것을 보실 수 있습니다.   아래 그림에서는 'Create DB (train)'이 현재 8% 진행되었고 전체적으로는 5분 34초 걸릴 것이라고 예상되어 있는 것을 보실 수 있습니다.  여기서 DB라 함은 LMDB를 말하는 것입니다.  LMDB는 Lightning Memory – Mapped Database라는 것으로서, 간단히 말하면 jpeg 등의 작은 파일로 된 data들을 담는 key-value store이며, 일반 DBMS는 아니지만 고속의 성능을 내기 때문에 이런 딥 러닝 training dataset을 저장하는데 자주 사용됩니다.



이렇게 LMDB가 생성되고 있는데, 그 data는 어떤 규칙에 의해서 만들어지고 있는 것일까요 ?  그건 오른쪽의 스크롤바를 내려 더 아래쪽으로 내려가보시면 볼 수 있습니다.  보시면 "Input File"이라는 항목에 train.txt라는 text file이 보이지요.  거기에는 각 png file 이름과 그에 따른 label이 정의되어 있고, 그에 따라 LMDB가 생성되고 있습니다.  
그 밑에는 그 카테고리, 즉 label에 해당하는 image file들의 숫자가 그래프로 표시되어 있고, 그 그래프 밑에는 'Explore DB'라는 버튼이 보입니다.



이 'Explore DB'라는 버튼을 클릭해보면 아래와 같이 0~9의 숫자마다 어떤 손글씨 그림이 들어있는지를 눈으로 보실 수 있습니다.



LMDB 생성 규칙을 담은 train.txt라는 link를 클릭해보면, 어떤 png file이 어느 숫자로 labeling되어 있는지 보실 수 있습니다.   아래 화면에서는 다 0으로 labeling 되어 있습니다.


좌측 상단의 DIGITS를 click하여 다시 홈페이지로 복귀합니다.   이제 MNIST 라는 dataset을 보실 수 있습니다.


다음으로는 model을 생성하겠습니다.   다음과 같이 Model tab에서 오른쪽 중단의 Images > Classification을 택합니다.


Dataset으로는 방금 생성한 MNIST를 선택합니다.



(여기서부터는 K80이 2장 장착된 Firestone 장비를 구했기 때문에 그것 기준으로 작성했습니다.)

더 밑으로 내려보면 아래와 같이 어떤 신경망을 이용해 training을 할 것인지 정하게 되어 있습니다.  여기서는 LeNet을 선택합니다.


그 밑으로는 몇개의 GPU를 사용하여 training할지 숫자를 적거나, 또는 현재 이 시스템에 장착된 GPU의 종류 및 개수를 보여주며 그 중 어느것을 사용할지 특정하여 선택하게 되어 있습니다.  여기서는 0번과 1번의 2개를 선택하겠습니다.  이렇게 해서 training될 모델의 이름은 LeNet으로 정하겠습니다.


'Create' 버튼을 클릭하면 training이 시작됩니다.   (위에서와는 달리 GPU가 장착된 서버이므로) 이젠 training 속도가 매우 빨라서 3분도 채 안 걸릴 것이라는 예상이 나오는 것을 보실 수 있습니다.


스크롤바를 밑으로 내려보면 accuracy와 loss가 실시간 graph로 보여집니다.  가로축은 epoch, 즉 전체 dataset을 몇번 반복해서 training했는지의 횟수인데, accuracy가 첫 epoch에서 이미 98%에 도달한 이후 더 개선이 없는 것을 보실 수 있습니다.   즉, 이런 경우는 여러번 반복 training하는 것이 큰 의미는 없습니다.  아무튼 여기서는 default인 30-epoch 반복하여 training합니다.


더 아래로 내리면 learning rate가 step size에 따라 계단식으로 줄어드는 것을 보실 수 있습니다.  또한 Caffe process ID 및 그것의 CPU 및 메모리 사용량도 보여줍니다.


아래 시점에서는 이미 19-epoch 반복 training이 끝났고, 그 snapshot이 저장되었기 때문에 'Trained Models'의 'Select Model' 항목에 Epoch #19가 display 되고 있는 것입니다.  Training이 원래대로 30회 반복되고 나면 저 숫자가 30까지 display 될 것입니다.



자, 30회 반복되어 training이 끝났습니다.  이제 trained model을 가지고 inference를 해볼 수 있습니다.  아래의 'Test a single image' 항목에서 Image Path에 실제 png file의 full path 이름을 입력합니다.






입력 후 'Classify One' 버튼을 누르면 새 tab이 생기면서 그 결과가 보여집니다.


여기서는 기존 training dataset 중 하나를 넣은 것이므로, 당연히 100% 정확한 답을 맞춥니다.


이번에는 다음과 같이 8과 1이라는 숫자의 이미지의 URL 주소를 가진 text 파일 'URL1.txt'를 upload하여 분류하도록 해보겠습니다.

http://www.clker.com/cliparts/V/1/Y/3/j/Z/blue-number-1-md.png
https://upload.wikimedia.org/wikipedia/commons/c/cf/NShw_8.png

이 URL에서 가리키는 URL은 각각 다음과 같습니다.





저 URL 주소를 가진 'URL1.txt'를 아래와 같이 'Upload Images List' 항목에 입력하고 'Classify Many'를 클릭합니다.


그 결과는 아래와 같습니다.   즉, 1과 8 대신 4와 9라고 답한 것이지요.  확실히 더 많은 image로 더 많은 training을 거쳐야 제대로 된 답을 내놓을 수 있습니다.


그렇다면 아래처럼 2글자로 된 이미지에는 뭐라고 답을 할까요 ?  참고로 이 LeNet이라는 모델은 하나의 숫자에 대해서만 training이 되어 있습니다.


이번에는 아래처럼 직접 image file을 upload하여 'Classify One' 버튼을 눌러 보겠습니다.  여기서는 특히 'Show visualizations and statistics' 체크 박스도 체크해보겠습니다.


결과는 아래와 같습니다.  3과 8에서 고민하다가 결국 3을 택한 것을 보실 수 있습니다.


그 아래로 내려보면 'Show visualizations and statistics'에 체크를 한 것 때문에 다양한 비주얼라이제이션과 통계치를 보실 수 있습니다.



digits에는 더욱 많은 기능들이 있으니 차츰 더 알아가 보시기 바랍니다.