2019년 10월 30일 수요일

H2O Driverless AI에서 HDFS로부터 dataset을 import 하기


H2O DAI에서 HDFS에 있는 파일을 직접 가져와야 하는 경우가 있습니다.  대부분의 경우 hadoop cluster의 data node들은 x86 아키텍처인 경우가 많은데, ppc64le 아키텍처인 IBM AC922 서버에서도 hadoop binary를 풀어놓기만 하면 쉽게 x86 아키텍처의  hadoop cluster에서 data를 끌어올 수 있습니다.  굳이 HDFS 속에 든 data file을 AC922 서버의 local filesystem(즉 POSIX filesystem)으로 copy해서 가져올 필요없이, H2O DAI의 web interface에서 직접 import 해올 수 있습니다.

이를 테스트해보기 위해 일단 다음과 같이 ppc64le 서버에 hadoop binary를 설치합니다.  여기서는 전에 올렸던 포스팅(http://hwengineer.blogspot.com/2019/01/ppc64le-redhat-x86-hadoop-cluster-hive.html)에서 빌드했던 ppc64le용 hadoop 2.6.5 binary를 사용하겠습니다.

[user654@p654-kvm1 ~]$ tar -xvf hadoop-2.6.5.tar.gz

[user654@p654-kvm1 ~]$ cd hadoop-2.6.5/etc/hadoop

다음과 같이 core-site.xml와 mapred-site.xml 등 일부 config 파일을 간단히 수정하여 이 서버를 hadoop name node이자 data node로 구성하겠습니다.

[user654@p654-kvm1 hadoop]$ vi mapred-site.xml
<configuration>
<property>
  <name>mapred.job.tracker</name>
  <value>localhost:9001</value>
 </property>

 <property>
  <name>mapred.local.dir</name>
  <value>${hadoop.tmp.dir}/mapred/local</value>
 </property>

 <property>
  <name>mapred.system.dir</name>
  <value>${hadoop.tmp.dir}/mapred/system</value>
 </property>
</configuration>

[user654@p654-kvm1 hadoop]$ vi core-site.xml
<configuration>
 <property>
  <name>fs.default.name</name>
  <value>hdfs://localhost:9000</value>
 </property>

 <property>
  <name>hadoop.tmp.dir</name>
  <value>/home/user654/hadoop-2.6.5/hadoop-${user.name}</value>
 </property>
</configuration>

[user654@p654-kvm1 hadoop]$ vi slaves
localhost

[user654@p654-kvm1 hadoop]$ cd ../..

이제 HDFS를 format 합니다.

[user654@p654-kvm1 hadoop-2.6.5]$ hadoop namenode -format

이어서 hadoop cluster를 시작합니다.

[user654@p654-kvm1 hadoop-2.6.5]$ . sbin/start-all.sh

OS user name인 user654의 home directory를 HDFS 내에 만듭니다.

[user654@p654-kvm1 ~]$ hadoop fs -mkdir -p /user/user654

이제 HDFS 내에 input이라는 directory를 만들고 거기에 OS의 filesystem에 들어있는 BlackFriday_train.xls 라는 file을 HDFS에 넣어줍니다.

[user654@p654-kvm1 ~]$ hadoop fs -mkdir input

[user654@p654-kvm1 ~]$ hadoop fs -put ./BlackFriday_train.xls input

다음과 같이 잘 들어간 것을 확인합니다.

[user654@p654-kvm1 ~]$ hadoop fs -ls -R
drwxr-xr-x   - user654 supergroup          0 2019-10-30 02:10 input
-rw-r--r--   3 user654 supergroup   10181120 2019-10-30 02:10 input/BlackFriday_train.xls


이제 H2O DAI의 구성 파일인 config.toml을 수정합니다.   고쳐야 할 부분은 core_site_xml_path과 hdfs_config_path에 hadoop config file들의 PATH를 명기하는 것 뿐입니다.

[user654@p654-kvm1 dai-1.8.0-linux-ppc64le]$ vi config.toml
...
core_site_xml_path = "/home/user654/hadoop-2.6.5/etc/hadoop"
...
hdfs_config_path = "/home/user654/hadoop-2.6.5/etc/hadoop"
...

이제 H2O DAI를 restart 합니다.

[user654@p654-kvm1 dai-1.8.0-linux-ppc64le]$ ./kill-dai.sh

[user654@p654-kvm1 dai-1.8.0-linux-ppc64le]$ ./run-dai.sh


그런 뒤에 Add Dataset 메뉴에 가서 HDFS를 선택합니다.   들어가보면 "Explore Hadoop File System"이라는 메뉴가 나옵니다.  이때 이 메뉴 제목 바로 아래의 hdfs:// 뒤에 다음과 같이 어느 hadoop node에 접근해야 하는지 이름은 손으로 타이핑해서 적어줘야 합니다.  여기서는 localhost:9000/ 까지 손으로 적어줍니다.  이후에는 directory 이름을 click 하여 원하는 파일을 HDFS에서 찾아낼 수 있습니다.

hdfs://localhost:9000/




다음과 같이 잘 import 되는 것을 보실 수 있습니다.


2019년 10월 10일 목요일

SRILM을 IBM POWER (ppc64le) 아키텍처의 Redhat에서 build하기


SRILM은 음성인식 등에 쓰이는 statistical language model(LM)들을 쉽게 구축하고 적용할 수 있는 toolkit입니다.  이것을 IBM POWER 서버에 설치된 Redhat이나 Ubuntu 등 linux OS에서도 build해서 사용할 수 있느냐에 대한 답변은 "Yes, 된다" 입니다.

SRILM의 현재 최신버전인 1.7.3 버전에서는 ppc64 (big-endian) 만 지원하는 것처럼 되어 있습니다만, 이는 그냥 ppc64le가 template에 아직 update가 안되었을 뿐이며, template만 약간 수정하면 ppc64le (little-endian)에서도 쉽게 build 하실 수 있습니다. 

먼저, 필요 OS fileset들을 설치합니다.  여기서는 Redhat 7.5를 기준으로 했습니다.

[user612@p612-met1 ~]$ sudo yum install -y gawk tcl-devel libticonv-devel bzip2

SRILM의 최신 버전 1.7.3을 아래 URL에서 download 받고, POWER 서버에 upload 합니다.

http://www.speech.sri.com/projects/srilm/download.html

압축을 해제합니다.

[user612@p612-met1 ~]$ mkdir srilm

[user612@p612-met1 ~]$ cd srilm

[user612@p612-met1 srilm]$ tar -xvf ../srilm-1.7.3.tar.gz

먼저 source에 포함된 machine-type 이라는 shell script를 수행해보면 MACHINE_TYPE을 못 찾겠다는 error가 납니다.  이는 아래와 같이 shell script를 약간만 수정하면 해결됩니다.

[user612@p612-met1 srilm]$ ./sbin/machine-type
could not determine MACHINE_TYPE

[user612@p612-met1 srilm]$ vi ./sbin/machine-type
...
            *)
                case "`uname -m`" in
                ppc64)  MACHINE_TYPE=ppc64
                        ;;
                ppc64le) MACHINE_TYPE=ppc64le    # Newly added
                        ;;                                       # Newly added
                i686)   MACHINE_TYPE=i686
                        ;;

이제 잘 됩니다.

[user612@p612-met1 srilm]$ ./sbin/machine-type
ppc64le

이어서 Makefile에서 SRILM home directory를 update 합니다.  사실 이건 ppc64le 아키텍처에서만 수정해야 하는 것이 아니라 x86 등 모든 아키텍처에서 다 환경에 맞게 수정해야 하는 부분입니다.

[user612@p612-met1 srilm]$ vi Makefile
...
# SRILM = /home/speech/stolcke/project/srilm/devel
SRILM = /home/user612/srilm    # Update per environment

ppc64le 아키텍처가 지원 안되는 것처럼 보이는 것은 아래의 common/Makefile.machine.* 파일들에 ppc64만 있고 ppc64le가 없기 때문입니다.  그냥 기존의 ppc64를 ppc64le로 copy해줍니다.

[user612@p612-met1 srilm]$ cp common/Makefile.machine.ppc64 common/Makefile.machine.ppc64le

그리고 여기서 LINK할 library에 pthread를 추가해줍니다.  (이를 안 해주면 어떤 error가 나는지는 맨 아래에 적었습니다.)

[user612@p612-met1 srilm]$ vi common/Makefile.machine.ppc64le
...
#   ADDITIONAL_LIBRARIES = -lm -ldl
   ADDITIONAL_LIBRARIES = -lm -ldl -lpthread   # Added -lpthread
...

이제 그냥 make를 수행하시면 됩니다.  아무 error 없이 잘 build 됩니다.

[user612@p612-met1 srilm]$ make -j16

이제 PATH를 잡아주고 ngram 명령을 수행해봅니다.  아래와 같이 잘 수행되는 것을 보실 수 있습니다.

[user612@p612-met1 srilm]$ pwd
/home/user612/srilm

[user612@p612-met1 srilm]$ export PATH=$PATH:/home/user612/srilm/bin/ppc64le

[user612@p612-met1 srilm]$ ngram -help
Usage of command "ngram"
 -version:                print version information
 -order:                  max ngram order
                Default value: 3
 -debug:                  debugging level for lm
                Default value: 0
 -skipoovs:               skip n-gram contexts containing OOVs
...



** 만약 위에서 common/Makefile.machine.ppc64le 속의 "ADDITIONAL_LIBRARIES = -lm -ldl -lpthread" 부분을 고치지 않는다면 다음과 같은 error를 겪게 되실 것입니다.  이 error가 나는 이유는 /usr/lib64/libpthread.so.0 가 참조되지 않기 때문이니, 위와 같이 common/Makefile.machine.ppc64le을 수정하시면 해결됩니다.

...
g++ -Wreturn-type -Wimplicit -DINSTANTIATE_TEMPLATES    -I. -I../../include   -u matherr -L../../lib/ppc64le  -g -O3 -o ../bin/ppc64le/ngram-merge ../obj/ppc64le/ngram-merge.o ../obj/ppc64le/liboolm.a -lm -ldl ../../lib/ppc64le/libflm.a ../../lib/ppc64le/libdstruct.a ../../lib/ppc64le/libmisc.a ../../lib/ppc64le/libz.a -ltcl -lm   2>&1 | c++filt
/usr/bin/ld: ../obj/ppc64le/liboolm.a(Vocab.o): undefined reference to symbol 'pthread_getspecific@@GLIBC_2.17'
//usr/lib64/libpthread.so.0: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
test -f ../bin/ppc64le/ngram-merge
make[2]: *** [../bin/ppc64le/ngram-merge] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: Leaving directory `/home/user612/srilm/lm/src'
make[1]: *** [release-programs] Error 1
make[1]: Leaving directory `/home/user612/srilm'
make: *** [World] Error 2

H2O DriverlessAI 에서의 custom recipe 등록하는 방법



H2O DriverlessAI (이하 H2O DAI) 버전 1.7 이상에서는 custom recipe를 등록해서 사용할 수 있는 기능, 즉 BYOR(Bring Your Own Recipe) 기능을 사용하실 수 있습니다.  이 recipe라는 것은 결국 python code인데, 자신만의 recipe를 어떤 format으로 어떻게 coding해야 하는지에 대해서는 다음의 github에 예제가 있으므로 참조하시기 바랍니다.

https://github.com/h2oai/driverlessai-recipes/


그렇게 recipe code를 작성하고 난 뒤에 어떻게 H2O DAI에 등록을 하느냐에 대해서 아래에 그림과 함께 정리했습니다.

우선은 custom recipe code를 새로 등록하기 전에, 기존의 recipe들에는 무엇이 있었는지 확인하겠습니다.

먼저 dataset을 선택해서 평소처럼 'Predict' 메뉴에 들어갑니다.  거기서 평소와는 달리 'EXPERT SETTINGS' 부분을 click 합니다.



그렇게 들어간 메뉴 오른쪽 상단에 'RECIPE' tab이 보입니다.  그걸 click 하십시요.



아래 그림과 같이 'Include specific models' 부분의 'SELECT VALUES' button을 click 하십시요.



그 결과를 보면 다음과 같습니다.  기본적으로 9개의 recipe가 이미 등록되어 있는 것을 보실 수 있습니다.  나중에 custom recipe를 등록한 뒤에 이 화면과 비교해보시면 됩니다.



이제 custome recipe code를 등록하겠습니다.  여기서는 여러분의 PC에 custom recipe를 위한 python code가 저장되어 있다고 가정하겠습니다.  'EXPERT SETTINGS' 메뉴의 맨 왼쪽 상단에 있는 '+ UPLOAD CUSTOM RECIPE' button을 click 하십시요.



그러면 upload할 code를 선택하기 위한 box가 나타납니다.  여기서 제가 upload할 code는 아래 github에서 가져온 것입니다.

https://github.com/h2oai/driverlessai-recipes/blob/master/models/algorithms/h2o-3-models.py

그 code를 선택하여 click 하시면 문법 등의 검증 과정을 거쳐 H2O DAI가 available한 recipe로 등록합니다.



이제 사실상 끝난 것입니다.   확인을 위해 아까 위에서 했던 것처럼 아래 그림과 같이 'Include specific models' 부분의 'SELECT VALUES' button을 click 하십시요.



이제 새로 upload된 python code 안의 custom model들이 새로 등록된 것을 보실 수 있습니다.



Default로는 모든 custom recipe도 enable되어 있습니다.  올리신 custom recipe들을 적용하고 싶지 않을 때는 오른쪽 상단의 'Disable/Enable Custom' toggle button을 클릭하시면 됩니다.

올리신 custom recipe를 제거하고 싶으시면 H2O DAI의 설치 direcoty 밑의 ./tmp/contrib/ 밑의 아래 내용들을 지우시면 됩니다.   그러신 뒤에 H2O DAI를 restart 하셔야 합니다.

$ pwd
/home/user612/dai-1.7.1-linux-ppc64le/tmp/contrib

$ rm -rf models_global_packages/* transformers_global_packages/* models/*



2019년 10월 8일 화요일

H2O DriverlessAI에서 큰 dataset을 학습할 때 memory를 적게 사용하도록 하는 설정 방법


H2O DriverlessAI는 서버 메모리를 많이 사용하는 솔루션입니다.  기본적인 rule of thumb은 training dataset의 10배 크기의 서버 메모리가 필요하다는 것입니다.  즉, 만약 training dataset이 30GB라면, 서버 메모리는 300GB 이상 있어야 합니다.

그런데 살다보면 training dataset이 30GB인데 서버 메모리는 100GB 밖에 안 되는 경우도 있습니다.  이런 경우에는 H2O DriverlessAI는 쓰지 못하는 것일까요?  꼭 그렇지는 않습니다.

H2O DriverlessAI는 기본적으로 'data가 너무 크다'라고 생각하면 down-sampling이라는 것을 합니다.  즉, data 중에서 일부만 발췌헤서 training에 사용하는 것입니다.

그렇다면 당연히 몇가지 의문이 생깁니다.

1.  '너무 크다'라는 것의 기준은 무엇인가?

당연히 기준이 있어야 하는데, 그건 H2O DriverlessAI의 config.toml이라는 파일 속에 있습니다.  이 파일은 H2O DriverlessAI의 설치 home directory에 들어있습니다.  기본적으로는 아래와 같이 statistical_threshold_data_size_large = 500000000 라는 parameter에 의해 정해지는데, 이 값은 default가 500M으로 되어 있고, 이 값은 data의 byte 수 등이 아니라 rows * columns 의 숫자입니다.   즉, 가령 50개의 column을 가진 CSV 파일의 row수가 10M을 넘어간다면 H2O DriverlessAI는 이 dataset이 '너무 크다' 라고 판단합니다.

[user612@p612-met1 dai-1.7.1-linux-ppc64le]$ vi ./config.toml
...
# Internal threshold for number of rows x number of columns to trigger certain statistical
# techniques (fewer genes created, removal of high max_depth for tree models, etc.) that can speed up modeling.
# Also controls maximum rows used in training final model,
# by sampling statistical_threshold_data_size_large / columns number of rows
#statistical_threshold_data_size_large = 500000000


2. 이 파라미터를 어떻게 얼마나 조정해야 하는가?

만약 이 기본값으로 training할 때 서버 메모리 부족으로 error가 난다면, 이 기준값을 내려야 합니다.  제가 해보니 이 수치가 정확하게 rows * columns를 맞추는 것은 아니니, 정확하게는 try & error를 통해 경험치로 조정하셔야 합니다.  아무튼 이것을 수정하는 방법은 저 config.toml 파일을 수정한 뒤 H2O DriverlessAI를 retstart 하는 것입니다.   아래의 예는 그 기준을 1/5f로 줄인 것입니다.

[user612@p612-met1 dai-1.7.1-linux-ppc64le]$ vi ./config.toml
...
#statistical_threshold_data_size_large = 500000000
statistical_threshold_data_size_large = 100000000


3. Sampling은 어떤 기준으로 이루어지는가? 

기본적으로 sampling 할 때는 알아서 적절히 하게 되어 있습니다.  특히 현재 training하려는 문제의 결과가 regression(결과가 숫자로 나오는 경우)의 경우와 classification(몇가지 정해진 카테고리 중 하나로 나오는 것)의 경우에 대해 각각 다릅니다.

- Regression의 경우 : Random sampling (마구잡이 샘플링)
- Classification의 경우 : Stratified sampling (계층별 샘플링)

그런데 특히 sparse data에 대한 classification인 경우, 즉 신용카드사의 정상/비정상 거래처럼 대부분은 정상이고 극히 일부만 비정상 거래인 경우에는 그렇쟎아도 부족한 비정상 거래 data 건수가 더 줄어들지 않을까 염려됩니다.  만약 그렇게 class별로 record 수가 5배 이상 차이가 날 경우, H2O DriverlessAI는 알아서 그 dataset을 imbalanced dataset이라고 판단하고 그에 따라 sampling을 진행합니다.

이런 imbalanced sampling는 "Expert Settings" tab의 "MODEL" 메뉴에서 조정이 가능하며, 다음과 같이 여러가지 방법을 제공합니다.




어떤 sampling 방법이 가장 좋은지에는 정답이 없습니다만, 위에서 언급한 신용카드 사기 거래 dataset과 같이 imbalance가 심한 dataset에서는  잘 판단이 서지 않을 때는 일단 Over_under_sampling을 택하는 것이 낫지 않을까 합니다.  (저는 일일이 테스트 해보지는 못했습니다.)

Automatic :  모든 class를 필요에 따라 적절히 sampling (default)
Over_sampling : 소수 class를 더 많이 sampling하여 균형을 맞춤
Under_sampling : 다수 class를 더 적게 sampling하여 균형을 맞춤 
Over_under_sampling : 소수 class는 over sampling하고 다수 class는 under sampling하여 균형을 맞춤
Off : Sampling을 하지 않음




4. 이렇게 down-sampling하면 결과로 만들어지는 accuracy에 저하가 발생하지 않는가 ?

당연히 일부 발생할 수도 있습니다.  그런 저하를 용납할 수 없는 상황이시라면 서버 메모리를 더 사셔야 합니다...



## Training의 메모리 필요량을 줄이는 다른 기본적인 방법들 

1.  Accuracy Dial을 낮춘다

- Accuracy를 높일 수록 좀더 많은 model을 적용해보기 때문에 당연히 메모리 사용량이 늘어납니다.  Accuracy dial을 3~4 정도로 낮게 두면 메모리 사용량이 극적으로 줄어듭니다.  그럴 경우 당연히 accuracy도 떨어진다는 단점이 있습니다.

2.  Expert Settings tab 중 'MODEL' 메뉴에서 lightGBM을 제외한 모든 model을 off 시킨다

- 무조건 많은 model을 적용한다고 accuracy가 다 높아지는 것은 아닙니다.  실제로 대부분의 경우 lightGBM과 XGBoost에서 가장 좋은 accuracy가 나옵니다.  따라서, Rulefit이나 GLM, Tensorflow 등의 모델들을 off 시켜 놓으면 역시 메모리 사용량이 극적으로 줄어듭니다.  물론 accuracy도 좀 떨어지긴 합니다.