레이블이 alexnet인 게시물을 표시합니다. 모든 게시물 표시
레이블이 alexnet인 게시물을 표시합니다. 모든 게시물 표시

2018년 3월 14일 수요일

Deep Learning용 GPU 서버 성능 측정을 위한 benchmark test 방법 몇가지

Deep Learning을 위한 GPU 서버들은 대부분 NVIDIA GPU를 사용하니까,  그 서버의 성능은 NVIDIA GPU 중 어떤 것을 몇 개 장착했느냐가 가장 중요합니다.  하지만 서버 벤더별 모델별로 그 특장점이 다 다르고 또 어떤 서버들은 NVLink 등 GPU와 GPU, GPU와 CPU의 연결 방식에 있어 차별점을 두고 있습니다.  어떤 경우엔 값비싼 최신 GPU를 잔뜩 달았지만 시스템 대역폭이 부족하여 제 성능을 내지 못할 수도 있고요.

이런 점을 확인해보려면 그냥 자신의 data를 이용하여 자신의 신경망을 직접 돌려보고 성능을 비교하는 것이 좋습니다만, 거기에는 많은 시간과 돈, 노력이 필요하지요.

여기서는 그런 점들을 비교적 쉽게 테스트해볼 수 있는 사실상 표준적인 벤치마크 방법론 몇가지를 소개합니다.  어느 경우든 linux의 time 명령을 이용해서 전체 수행 시간만 측정하면 되니까 매우 편리합니다.   평가 대상이 신경망 자체가 아니라 GPU 서버 하드웨어이고 다 같은 data와 같은 신경망을 사용하니까 테스트 결과의 accuracy 등은 비교해 보실 필요가 없습니다.

아래 테스트들은 tensorflow 1.4.1, 그리고 Anaconda3에서 제공되는 python3.6을 사용해서 수행한 것입니다.

[user1@gpusvr ~]$ which python
~/anaconda3/bin/python

먼저, 다음과 같이 github로부터 tensorflow models를 clone 합니다.

[user1@gpusvr ~]$ git clone https://github.com/tensorflow/models.git


1. CIFAR10

이중 가장 많이 사용되는 것이 cifar10입니다.  수행할 cifar10_multi_gpu_train.py code를 열어보면 조정 가능한 parameter가 무엇이고 그 default 값이 무엇인지 보실 수 있습니다.  그를 명령어에서 적절히 수정하여 수행하시면 됩니다. 

[user1@gpusvr ~]$ cd models/tutorials/image/cifar10

[user1@gpusvr cifar10]$  vi cifar10_multi_gpu_train.py
...
parser.add_argument('--max_steps', type=int, default=1000000, 
...
parser.add_argument('--num_gpus', type=int, default=1, 

이제 아래와 같이 수행합니다.  여기서는 # max_steps=10000 num_gpus=4 으로 수행합니다.  다만, 처음에 이 테스트를 수행할 때는 internet에서 dataset을 download 받는 것부터 시작합니다.  그 부분은 당연히 테스트 수행 시간에서 제외해야 합니다.  따라서 처음 테스트는 측정하지 마시고, 2번째 테스트 이후를 측정하시면 됩니다.  그때는 이미 download 받아놓은 data를 사용하거든요.

[user1@gpusvr cifar10]$ time python cifar10_multi_gpu_train.py --max_steps=10000 --num_gpus=4 --batch_size 512
...
2018-03-09 11:36:47.084991: step 9990, loss = 0.58 (25710.1 examples/sec; 0.020 sec/batch)

이렇게 수행해보면 GPU 사용량은 17~22% 정도로 상당히 낮습니다.  batch_size를 8배인 4096으로 키워도 되는데, 이때 그에 따라 max_steps도 1/8로 줄여야 합니다.  아래는 # max_steps=1250 num_gpus=4 으로 수행한 것입니다.

[user1@gpusvr cifar10]$ time python cifar10_multi_gpu_train.py --max_steps=1250 --num_gpus=4 --batch_size 4096 
...
2018-03-09 13:11:57.589629: step 1240, loss = 2.08 (28222.4 examples/sec; 0.145 sec/batch)

성능이 조금 나아지긴 합니다만, 극적으로 나아지진 않습니다.


2. Alexnet

여기에 포함된 alexnet_benchmark.py은 single GPU만 이용합니다.

[user1@gpusvr alexnet]$ time python alexnet_benchmark.py --batch_size=1024 --num_batches=1000
...
2018-03-09 14:32:56.566184: step 990, duration = 0.522
2018-03-09 14:33:01.260806: Forward-backward across 1000 steps, 0.521 +/- 0.002 sec / batch

그러나 여기서도 다음과 같이 script를 짜서 여러개의 GPU를 사용하는 벤치마크를 할 수 있습니다.  CUDA_VISIBLE_DEVICES 환경변수를 이용하여 각 세션마다 특정 GPU를 할당한 뒤, GPU 개수만큼 alexnet_benchmark.py를 병렬로 수행하는 것입니다.  이것도 의미가 있는 테스트입니다.  실제 대부분의 고객들이 여러개의 GPU가 달린 서버를 사용할 때, 여러개의 GPU를 이용하여 하나의 model을 training하는 경우보다는 아래 script처럼 여러 연구원이 1개씩의 GPU를 가지고 각자의 training을 수행하는 경우가 대부분이기 때문입니다.  그런 경우에도 CPU-GPU 간의 병목 없이 원활한 성능이 나오는지 확인하는 것도 중요합니다.

[user1@gpusvr alexnet]$ vi alexrun.sh
echo "Starting !"
CUDA_VISIBLE_DEVICES=0 python alexnet_benchmark.py --batch_size=1024 --num_batches=1000 &
CUDA_VISIBLE_DEVICES=1 python alexnet_benchmark.py --batch_size=1024 --num_batches=1000 &
CUDA_VISIBLE_DEVICES=2 python alexnet_benchmark.py --batch_size=1024 --num_batches=1000 &
CUDA_VISIBLE_DEVICES=3 python alexnet_benchmark.py --batch_size=1024 --num_batches=1000 &
wait
echo "Completed !"

[user1@gpusvr alexnet]$ chmod a+x alexrun.sh
[user1@gpusvr alexnet]$ time ./alexrun.sh
Starting !
....
2018-03-09 14:50:15.533991: step 990, duration = 0.523
2018-03-09 14:50:17.971840: step 990, duration = 0.521
2018-03-09 14:50:18.197058: step 990, duration = 0.524
2018-03-09 14:50:20.202855: step 990, duration = 0.525
2018-03-09 14:50:20.231726: Forward-backward across 1000 steps, 0.522 +/- 0.001 sec / batch
2018-03-09 14:50:22.691048: Forward-backward across 1000 steps, 0.524 +/- 0.002 sec / batch
2018-03-09 14:50:22.908471: Forward-backward across 1000 steps, 0.523 +/- 0.002 sec / batch
2018-03-09 14:50:24.927234: Forward-backward across 1000 steps, 0.525 +/- 0.002 sec / batch
Completed !

실제 수행 결과를 보면 아무래도 1개 GPU만 사용했을 때보다 약간 더 느리게 나오는 것을 보실 수 있습니다.


3. RNN PTB  

이것도 1개의 GPU만 이용하는 benchmark test입니다.  다른 것과는 달리 image에 대한 CNN 트레이닝이 아니라 text에 대한 RNN 트레이닝이라는 점이 주목할 만 합니다.

[user1@gpusvr ptb]$ pwd
/home/user1/models/tutorials/rnn/ptb

이 테스트를 위한 sample data는 아래에서 따로 download 받아야 합니다.

[user1@gpusvr ptb]$ wget http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz

[user1@gpusvr ptb]$ tar xvfz simple-examples.tgz -C $HOME

[user1@gpusvr ptb]$ du ~/simple-examples
17256   /home/user1/simple-examples/data
74840   /home/user1/simple-examples/models
516     /home/user1/simple-examples/rnnlm-0.2b
12      /home/user1/simple-examples/1-train
12      /home/user1/simple-examples/3-combination
0       /home/user1/simple-examples/2-nbest-rescore/lattices/nbest
2860    /home/user1/simple-examples/2-nbest-rescore/lattices
2900    /home/user1/simple-examples/2-nbest-rescore
12      /home/user1/simple-examples/5-one-iter
12      /home/user1/simple-examples/6-recovery-during-training
12      /home/user1/simple-examples/7-dynamic-evaluation
0       /home/user1/simple-examples/temp
12      /home/user1/simple-examples/8-direct
12      /home/user1/simple-examples/4-data-generation
12      /home/user1/simple-examples/9-char-based-lm
95608   /home/user1/simple-examples

Training에 사용되는 data는 총 94MB 정도로서 작은 편입니다.

[user1@gpusvr ptb]$ du -sm ~/simple-examples
94      /home/user1/simple-examples

Data 내용은 아래에 보시다시피 text들입니다.

[user1@gpusvr ~]$ cd /home/user1/simple-examples/data

[user1@gpusvr data]$ head ptb.train.txt
 aer banknote berlitz calloway centrust cluett fromstein gitano guterman hydro-quebec ipo kia memotec mlx nahb punts rake regatta rubens sim snack-food ssangyong swapo wachter
 pierre <unk> N years old will join the board as a nonexecutive director nov. N
 mr. <unk> is chairman of <unk> n.v. the dutch publishing group
 rudolph <unk> N years old and former chairman of consolidated gold fields plc was named a nonexecutive director of this british industrial conglomerate
 a form of asbestos once used to make kent cigarette filters has caused a high percentage of cancer deaths among a group of workers exposed to it more than N years ago researchers reported
 the asbestos fiber <unk> is unusually <unk> once it enters the <unk> with even brief exposures to it causing symptoms that show up decades later researchers said
 <unk> inc. the unit of new york-based <unk> corp. that makes kent cigarettes stopped using <unk> in its <unk> cigarette filters in N
 although preliminary findings were reported more than a year ago the latest results appear in today 's new england journal of medicine a forum likely to bring new attention to the problem
 a <unk> <unk> said this is an old story
 we 're talking about years ago before anyone heard of asbestos having any questionable properties

[user1@gpusvr data]$ head ptb.char.train.txt
a e r _ b a n k n o t e _ b e r l i t z _ c a l l o w a y _ c e n t r u s t _ c l u e t t _ f r o m s t e i n _ g i t a n o _ g u t e r m a n _ h y d r o - q u e b e c _ i p o _ k i a _ m e m o t e c _ m l x _ n a h b _ p u n t s _ r a k e _ r e g a t t a _ r u b e n s _ s i m _ s n a c k - f o o d _ s s a n g y o n g _ s w a p o _ w a c h t e r
 p i e r r e _ < u n k > _ N _ y e a r s _ o l d _ w i l l _ j o i n _ t h e _ b o a r d _ a s _ a _ n o n e x e c u t i v e _ d i r e c t o r _ n o v . _ N
 m r . _ < u n k > _ i s _ c h a i r m a n _ o f _ < u n k > _ n . v . _ t h e _ d u t c h _ p u b l i s h i n g _ g r o u p
 r u d o l p h _ < u n k > _ N _ y e a r s _ o l d _ a n d _ f o r m e r _ c h a i r m a n _ o f _ c o n s o l i d a t e d _ g o l d _ f i e l d s _ p l c _ w a s _ n a m e d _ a _ n o n e x e c u t i v e _ d i r e c t o r _ o f _ t h i s _ b r i t i s h _ i n d u s t r i a l _ c o n g l o m e r a t e
 a _ f o r m _ o f _ a s b e s t o s _ o n c e _ u s e d _ t o _ m a k e _ k e n t _ c i g a r e t t e _ f i l t e r s _ h a s _ c a u s e d _ a _ h i g h _ p e r c e n t a g e _ o f _ c a n c e r _ d e a t h s _ a m o n g _ a _ g r o u p _ o f _ w o r k e r s _ e x p o s e d _ t o _ i t _ m o r e _ t h a n _ N _ y e a r s _ a g o _ r e s e a r c h e r s _ r e p o r t e d
 t h e _ a s b e s t o s _ f i b e r _ < u n k > _ i s _ u n u s u a l l y _ < u n k > _ o n c e _ i t _ e n t e r s _ t h e _ < u n k > _ w i t h _ e v e n _ b r i e f _ e x p o s u r e s _ t o _ i t _ c a u s i n g _ s y m p t o m s _ t h a t _ s h o w _ u p _ d e c a d e s _ l a t e r _ r e s e a r c h e r s _ s a i d
 < u n k > _ i n c . _ t h e _ u n i t _ o f _ n e w _ y o r k - b a s e d _ < u n k > _ c o r p . _ t h a t _ m a k e s _ k e n t _ c i g a r e t t e s _ s t o p p e d _ u s i n g _ < u n k > _ i n _ i t s _ < u n k > _ c i g a r e t t e _ f i l t e r s _ i n _ N
 a l t h o u g h _ p r e l i m i n a r y _ f i n d i n g s _ w e r e _ r e p o r t e d _ m o r e _ t h a n _ a _ y e a r _ a g o _ t h e _ l a t e s t _ r e s u l t s _ a p p e a r _ i n _ t o d a y _ ' s _ n e w _ e n g l a n d _ j o u r n a l _ o f _ m e d i c i n e _ a _ f o r u m _ l i k e l y _ t o _ b r i n g _ n e w _ a t t e n t i o n _ t o _ t h e _ p r o b l e m
 a _ < u n k > _ < u n k > _ s a i d _ t h i s _ i s _ a n _ o l d _ s t o r y
 w e _ ' r e _ t a l k i n g _ a b o u t _ y e a r s _ a g o _ b e f o r e _ a n y o n e _ h e a r d _ o f _ a s b e s t o s _ h a v i n g _ a n y _ q u e s t i o n a b l e _ p r o p e r t i e s

이제 training을 해보겠습니다.  아래와 같이 전체 training 및 test에 걸린 시간을 측정하면 됩니다.

[user1@gpusvr ptb]$ time python ptb_word_lm.py --data_path=$HOME/simple-examples/data/ --model=small
...
Epoch: 1 Learning rate: 1.000
...
Epoch: 1 Train Perplexity: 268.322
Epoch: 1 Valid Perplexity: 178.848
Epoch: 2 Learning rate: 1.000
...
Epoch: 13 Train Perplexity: 40.549
Epoch: 13 Valid Perplexity: 119.536
Test Perplexity: 114.159

위의 Alexnet 테스트에서처럼, 이 test도 여러개의 세션을 동시에 수행함으로써 시스템 대역폭이 충분하여 multi-user 동시 사용시에도 충분한 성능을 내는지 확인해보실 수 있습니다.

[user1@gpusvr ptb]$ vi ptbrun.sh
echo "Starting !"
CUDA_VISIBLE_DEVICES=0 python ptb_word_lm.py --data_path=$HOME/simple-examples/data/ --model=small &
CUDA_VISIBLE_DEVICES=1 python ptb_word_lm.py --data_path=$HOME/simple-examples/data/ --model=small &
CUDA_VISIBLE_DEVICES=2 python ptb_word_lm.py --data_path=$HOME/simple-examples/data/ --model=small &
CUDA_VISIBLE_DEVICES=3 python ptb_word_lm.py --data_path=$HOME/simple-examples/data/ --model=small &
wait
echo "Completed !"

실제로 아래와 같이 수행해보면 single session으로 single GPU를 이용했을 때에 비해 약간 성능이 떨어지는 것을 보실 수 있습니다.

[user1@gpusvr ptb]$ time ./ptbrun.sh
Starting !
...
Epoch: 13 Valid Perplexity: 119.785
...
Test Perplexity: 113.431
Completed !

2017년 12월 12일 화요일

Caffe를 이용하여 ILSVRC2012 dataset을 alexnet으로 training하기

먼저 작업 환경을 PowerAI에 포함된 caffe-nv로 하기 위해 PATH 등 각종 환경 변수를 설정해주는 다음 script를 수행합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ source /opt/DL/caffe-nv/bin/caffe-activate

다음과 같이 caffe가 caffe-nv로 잡히는지 확인합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ which caffe
/opt/DL/caffe-nv/bin/caffe

PowerAI에 포함된 caffe-nv 밑의 example과 data를 GPFS 파일시스템 쪽으로 copy해옵니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ cp -r /opt/DL/caffe-nv/examples examples
b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ cp -r /opt/DL/caffe-nv/data data

거기서 아래와 같이 get_ilsvrc_aux.sh를 수행하여 ilsvrc2012 dataset 생성에 필요한 label 파일 등을 download 받습니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ cd data/ilsvrc12
b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za/data/ilsvrc12$ ./get_ilsvrc_aux.sh

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za/data/ilsvrc12$ ls -ltr
total 37888
-rw-r----- 1 b7p286za IBM1  3200000 Feb 25  2014 test.txt
-rw-r----- 1 b7p286za IBM1    10000 Feb 25  2014 synsets.txt
-rw-r----- 1 b7p286za IBM1   786446 Feb 25  2014 imagenet_mean.binaryproto
-rw-r----- 1 b7p286za IBM1  1644500 Feb 25  2014 val.txt
-rw-r----- 1 b7p286za IBM1 43829433 Feb 25  2014 train.txt
-rw-r----- 1 b7p286za IBM1    31675 Apr  8  2014 synset_words.txt
-rw-r----- 1 b7p286za IBM1     3787 Jun  8  2014 det_synset_words.txt
-rw-r----- 1 b7p286za IBM1 14931117 Jul 11  2014 imagenet.bet.pickle
-rwxr-x--- 1 b7p286za IBM1      585 Dec 12 02:12 get_ilsvrc_aux.sh

이제 imagenet data, 즉 ILSVRC2012를 download 받습니다.  Training dataset은 앞선 posting에서 사용한 tensorflow resnet training에서 사용했던 raw-data를 이용하면 됩니다.  다만, 거기서는 validation dataset도 label명에 따른 디렉토리로 분산해서 넣었는데, 이 alexnet에서는 val이라는 디렉토리에 한꺼번에 풀어놓아야 합니다.  따라서 다음과 같이 val만 새로 풀어놓습니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za/data/ilsvrc12$ cd ../..

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ mkdir raw-data/val && cd raw-data/val

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za/raw-data/val$ tar -xf ../../ILSVRC2012_img_val.tar

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za/raw-data/val$ cd ../..

이제 raw-data 밑의 train과 val 속의 JPEG 파일들을 LMDB 포맷으로 변환해야 합니다.  다음과 같이 create_imagenet.sh 스크립트를 수정해서 사용합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ vi ./examples/imagenet/create_imagenet.sh
...
export CAFFE_BIN=/opt/DL/caffe-nv/bin   (추가)
...
TRAIN_DATA_ROOT=/gpfs/gpfs_gl4_16mb/b7p286za/raw-data/train/
VAL_DATA_ROOT=/gpfs/gpfs_gl4_16mb/b7p286za/raw-data/val/
...
#RESIZE=false
RESIZE=true

수정을 마치고 다음과 같이 수행합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ time ./examples/imagenet/create_imagenet.sh

이 과정도 200GB가 넘는 data를 LMDB format으로 변환하는 것이므로 스토리지 상황에 따라 6~7시간 가량 걸립니다.   위의 script가 다 돌고나면 examples/imagenet/ilsvrc12_train_lmdb와 examples/imagenet/ilsvrc12_val_lmdb에 LMDB format으로 변환된 dataset이 생깁니다.

이제 생성된 LMDB로부터 전체 imagenet data의 평균값을 구하기 위해 make_imagenet_mean.sh를 수행합니다.  여기서도 script 맨 앞에 다음과 같이 CAFFE_BIN을 정의해줍니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ vi ./examples/imagenet/make_imagenet_mean.sh
source /opt/DL/caffe-nv/bin/caffe-activate
export CAFFE_BIN=/opt/DL/caffe-nv/bin
...

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ time ./examples/imagenet/make_imagenet_mean.sh

다음으로는 solver.prototxt를 수정합니다.  먼저 /opt/DL/caffe-nv/models에 있는 bvlc_alexnet 디렉토리를 GPFS 파일시스템으로 copy해옵니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ cp -r /opt/DL/caffe-nv/models/bvlc_alexnet .

그리고나서 다음과 같이 solver.prototxt 속의 디렉토리 이름들과 max_iter 등을 적절히 수정해줍니다.
여기서는 나중에 batch_size를 2048로 할 것이므로, max_iter를 1250으로 하면 대략 1250 x 2048 / 1280000 = 20 epochs의 training을 완료하게 됩니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ vi bvlc_alexnet/solver.prototxt
#net: "models/bvlc_alexnet/train_val.prototxt"
net: "bvlc_alexnet/train_val.prototxt"
...
#display: 20
display: 500
#max_iter: 100000
max_iter: 1250
...
#snapshot_prefix: "models/bvlc_alexnet/caffe_alexnet_train"
snapshot_prefix: "bvlc_alexnet/caffe_alexnet_train"

다음으로는 bvlc_alexnet/train_val.prototxt를 필요시 수정하여 train data의 batch_size를 늘이거나 줄이고, 각종 path도 적절히 변경합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ vi bvlc_alexnet/train_val.prototxt
...
    source: "examples/imagenet/ilsvrc12_train_lmdb"
#    batch_size: 1024
    batch_size: 2048
...

이제 다음과 같은 train_alexnet.sh를 만들어 수행합니다.

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ vi ./examples/imagenet/train_alexnet.sh
source /opt/DL/caffe-nv/bin/caffe-activate
export CAFFE_BIN=/opt/DL/caffe-nv/bin
set -e
$CAFFE_BIN/caffe train -gpu all --solver=bvlc_alexnet/solver.prototxt

b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ nohup time ./examples/imagenet/train_alexnet.sh &

결과 log는 nohup.out에서 보실 수 있습니다.   위와 같이 20 epochs를 수행하는데는 12분 정도 밖에 안 걸립니다. 


b7p286za@p10login1:/gpfs/gpfs_gl4_16mb/b7p286za$ grep iter nohup.out
test_iter: 1000
max_iter: 1250
I1212 14:15:42.151552 82131 solver.cpp:242] Iteration 0 (0 iter/s, 24.031s/500 iter), loss = 6.91103
I1212 14:22:10.507652 82131 solver.cpp:242] Iteration 500 (1.28749 iter/s, 388.352s/500 iter), loss = 6.37464
I1212 14:26:19.506183 82131 solver.cpp:242] Iteration 1000 (2.00806 iter/s, 248.996s/500 iter), loss = 5.34417
I1212 14:27:50.453514 82131 solver.cpp:479] Snapshotting to binary proto file bvlc_alexnet/caffe_alexnet_train_iter_1250.caffemodel
I1212 14:27:51.540899 82131 sgd_solver.cpp:273] Snapshotting solver state to binary proto file bvlc_alexnet/caffe_alexnet_train_iter_1250.solverstate


2017년 10월 27일 금요일

caffe DDL을 이용한 Alexnet training

지난 9월 포스팅(http://hwengineer.blogspot.kr/2017/09/ibm-powerai-40-caffe-distributed-deep.html)에서 PowerAI에 포함된 DDL(Distributed Deep Learning), 즉 MPI를 이용한 분산처리 기능에 대해 간단히 설명드린 바 있습니다.  이번에는 그것으로 ILSVRC2012의 128만장 image dataset을 caffe alexnet으로 training 해보겠습니다.

제가 잠깐 빌릴 수 있는 Minsky 서버가 딱 1대 뿐이라, 원래 여러대의 Minsky 서버를 묶어서 하나의 model을 train시킬 수 있지만 여기서는 1대의 서버에서 caffe DDL을 수행해보겠습니다.  잠깐, 1대라고요 ?  1대에서 MPI 분산처리가 의미가 있나요 ?

예, 없지는 않습니다.  Multi-GPU를 이용한 training을 할 때 일반 caffe와 caffe DDL의 차이는 multi-thread냐, multi-process냐의 차이입니다.  좀더 쉽게 말해, 일반 caffe에서는 하나의 caffe process가 P2P를 통해 여러개의 GPU를 사용합니다.  그에 비해, caffe DDL에서는 GPU당 1개씩 별도의 caffe process가 떠서, 서로간에 MPI를 이용한 통신을 하며 여러개의 GPU를 사용합니다.

이를 그림으로 표현하면 아래와 같습니다.



실제로, 일반 caffe를 사용할 경우 nvidia-smi로 관찰해보면 다음과 같이 caffe의 PID가 모두 같지만, caffe DDL에서는 각 GPU를 사용하는 caffe PID가 서로 다릅니다.

일반 caffe :

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     30681    C   caffe                                        15497MiB |
|    1     30681    C   caffe                                        14589MiB |
|    2     30681    C   caffe                                        14589MiB |
|    3     30681    C   caffe                                        14589MiB |
+-----------------------------------------------------------------------------+

caffe DDL :

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     31227    C   caffe                                        15741MiB |
|    1     31228    C   caffe                                        14837MiB |
|    2     31229    C   caffe                                        14837MiB |
|    3     31230    C   caffe                                        14837MiB |
+-----------------------------------------------------------------------------+

자, 대략 차이를 이해하셨으면, alexnet training을 한번은 일반 caffe로, 또 한번은 caffe DDL로 training해보시지요.  물론 모두 같은 Minsky 서버, 즉 4-GPU 시스템 1대를 써서 테스트한 것입니다.  각각의 성능 측정은 128만장을 2-epochs, 즉 2회 반복 training할 때 걸린 시간으로 측정하겠습니다.

일반 caffe :

test@ubuntu:/nvme$ caffe train --solver=models/bvlc_alexnet/solver.prototxt -gpu all

caffe DDL :

test@ubuntu:/nvme$ mpirun -x PATH -x LD_LIBRARY_PATH -n 4 -rf 4x1x1.rf caffe train --solver=models/bvlc_alexnet/solver.prototxt -gpu 0 -ddl "-mode n:4x1x1 -dev_sync 1"

지난번에 잠깐 설명드린 것을 반복하자면 이렇습니다.

- mpirun은 여러대의 서버 노드에 동일한 명령을 동일한 환경변수 (-x 옵션)을 써서 수행해주는 병렬환경 명령어입니다.
- 4x1x1.rf라는 이름의 파일은 rank file입니다.  이 속에 병렬 서버 환경의 toplogy가 들어있습니다.
- -n 4라는 것은 MPI client의 총 숫자이며, 쉽게 말해 training에 이용하려는 GPU의 갯수입니다.
- -gpu 0에서, 왜 4개가 아니라 gpu 0이라고 1개로 지정했는지 의아하실 수 있는데, MPI 환경에서는 각각의 GPU가 하나의 learner가 됩니다.  따라서 실제 물리적 서버 1대에 GPU가 몇 장 장착되어있든 상관없이 모두 -gpu 0, 즉 GPU는 1개로 지정한 것입니다.
- "-mode b:4x1x1"에서 b라는 것은 가능하면 enhanced NCCL을 이용하라는 뜻입니다.  4x1x1은 4장의 GPU를 가진 서버 1대가 하나의 rack에 들어있다는 뜻입니다.
- dev_sync에서 0은 GPU간 sync를 하지 말라는 것이고, 1은 통신 시작할 때 sync하라는 뜻, 2는 시작할 때와 끝낼 때 각각 sync하라는 뜻입니다.

여기서 사용된 rank file 4x1x1.rf 속의 내용은 아래와 같습니다.

rank 0=minksy           slot=0:0-3
rank 1=minksy           slot=0:4-7
rank 2=minksy           slot=1:0-3
rank 3=minksy           slot=1:4-7

128만장 x 2-epochs를 처리하기 위해서는, solver.prototxt와 train_val.prototxt 속에 표시된 batch_size와 max_iter의 곱이 128만장 x 2 = 256만장이면 됩니다.  batch_size를 조절함에 따라 training 속도가 꽤 달라지는데, 여기서는 256부터 512, 768 순으로 늘려가며 테스트해보겠습니다.



위 표에서 보시다시피, batch_size가 작을 때는 일반 caffe의 성능이 더 빨랐는데, batch_size가 점점 커지면서 caffe DDL의 성능이 점점 더 빨라져서 결국 역전하게 됩니다.  batch_size와 MPI를 이용한 DDL의 성능과의 상관 관계가 있을 것 같기는 한데, 아직 그 이유는 파악을 못 했습니다.  Lab에 문의해봤는데, batch_size와는 무관할 것이라는 답변을 받긴 했습니다.


여기서 batch_size를 1024보다 더 키우면 어떻게 될까요 ?

...
F1025 17:48:43.059572 30265 syncedmem.cpp:651] Check failed: error == cudaSuccess (2 vs. 0)  out of memoryF1025 17:48:43.071281 30285 syncedmem.cpp:651] Check failed: error == cudaSuccess (2 vs. 0)  out of memory
*** Check failure stack trace: ***
    @     0x3fffb645ce0c  google::LogMessage::Fail()
    @     0x3fffb69649cc  caffe::Solver<>::Step()
    @     0x3fffb645f284  google::LogMessage::SendToLog()
...

일반 caffe든 caffe DDL이든 batch_size가 1100만 되어도 이렇게 out-of-memory (OOM) error를 내며 죽어버립니다.  그러니 아쉽게도 더 큰 batch_size에서는 테스트가 안되는 것이지요.

batch_size가 너무 커서 OOM error가 난다면 그걸 또 피해가는 방법이 있습니다.  역시 caffe-ibm에 포함된 LMS(large model support)입니다.   아래와 같이 -lms 옵션을 주면 caffe나 caffe DDL이나 모두 batch_size=1200 정도까지는 무난히 돌릴 수 있습니다.  -lms 800000이라는 것은 800000KB 이상의 memory chunk는 GPU 말고 CPU에 남겨두라는 뜻입니다.   (http://hwengineer.blogspot.kr/2017/09/inference-gpu-sizing-ibm-caffe-large.html 참조)


일반 caffe with LMS :

test@ubuntu:/nvme$ caffe train -lms 800000 --solver=models/bvlc_alexnet/solver.prototxt -gpu all

caffe DDL with LMS :

test@ubuntu:/nvme$ mpirun -x PATH -x LD_LIBRARY_PATH -n 4 -rf 4x1x1.rf caffe train -lms 800000 --solver=models/bvlc_alexnet/solver.prototxt -gpu 0 -ddl "-mode n:4x1x1 -dev_sync 1"


그 결과는 아래와 같습니다.   확실히 batch_size가 커질 수록 일반 caffe보다 caffe DDL의 성능이 더 잘 나옵니다.




궁금해하실 분들을 위해서, caffe DDL을 수행할 경우 나오는 메시지의 앞부분과 뒷부분 일부를 아래에 붙여놓습니다.

--------------------------------------------------------------------------
[[31653,1],2]: A high-performance Open MPI point-to-point messaging module
was unable to find any relevant network interfaces:

Module: OpenFabrics (openib)
  Host: minsky

Another transport will be used instead, although this may result in
lower performance.
--------------------------------------------------------------------------
ubuntu: n0(0) n1(0) n2(0) n3(0) 
I1025 18:59:45.681555 31227 caffe.cpp:151] [MPI:0   ] spreading GPUs per MPI rank
I1025 18:59:45.681725 31227 caffe.cpp:153] [MPI:0   ]    use gpu[0]
I1025 18:59:45.681541 31228 caffe.cpp:151] [MPI:1   ] spreading GPUs per MPI rank
I1025 18:59:45.681725 31228 caffe.cpp:153] [MPI:1   ]    use gpu[1]
I1025 18:59:45.681541 31229 caffe.cpp:151] [MPI:2   ] spreading GPUs per MPI rank
I1025 18:59:45.681726 31229 caffe.cpp:153] [MPI:2   ]    use gpu[2]
I1025 18:59:45.681735 31229 caffe.cpp:283] Using GPUs 2
I1025 18:59:45.681541 31230 caffe.cpp:151] [MPI:3   ] spreading GPUs per MPI rank
I1025 18:59:45.681726 31230 caffe.cpp:153] [MPI:3   ]    use gpu[3]
I1025 18:59:45.681735 31230 caffe.cpp:283] Using GPUs 3
I1025 18:59:45.681735 31227 caffe.cpp:283] Using GPUs 0
I1025 18:59:45.681733 31228 caffe.cpp:283] Using GPUs 1
I1025 18:59:45.683846 31228 caffe.cpp:288] GPU 1: Tesla P100-SXM2-16GB
I1025 18:59:45.683897 31227 caffe.cpp:288] GPU 0: Tesla P100-SXM2-16GB
I1025 18:59:45.683955 31230 caffe.cpp:288] GPU 3: Tesla P100-SXM2-16GB
I1025 18:59:45.684010 31229 caffe.cpp:288] GPU 2: Tesla P100-SXM2-16GB
I1025 18:59:46.056959 31227 caffe.cpp:302] [MPI:0   ]  name = minsky root = 1
I1025 18:59:46.067212 31228 caffe.cpp:302] [MPI:1   ]  name = minsky root = 1
I1025 18:59:46.070734 31230 caffe.cpp:302] [MPI:3   ]  name = minsky root = 1
I1025 18:59:46.071211 31229 caffe.cpp:302] [MPI:2   ]  name = minsky root = 1
I1025 18:59:46.073958 31227 solver.cpp:44] Initializing solver from parameters: 
test_iter: 1000
test_interval: 1000
base_lr: 0.01
display: 500
max_iter: 2500
lr_policy: "step"
gamma: 0.1

...중략...

I1025 19:24:53.536928 31227 solver.cpp:414]     Test net output #0: accuracy = 0.20032
I1025 19:24:53.536965 31227 solver.cpp:414]     Test net output #1: loss = 4.09802 (* 1 = 4.09802 loss)
I1025 19:24:54.180562 31227 solver.cpp:223] Iteration 2000 (1.27922 iter/s, 390.864s/500 iters), loss = 4.18248
I1025 19:24:54.180598 31227 solver.cpp:242]     Train net output #0: loss = 4.18248 (* 1 = 4.18248 loss)
I1025 19:24:54.180613 31227 sgd_solver.cpp:121] Iteration 2000, lr = 0.01
I1025 19:26:57.349701 31256 data_layer.cpp:86] Restarting data prefetching from start.
I1025 19:30:28.547333 31256 data_layer.cpp:86] Restarting data prefetching from start.
I1025 19:30:29.081480 31227 solver.cpp:466] Snapshotting to binary proto file models/bvlc_alexnet/caffe_alexnet_train_iter_2500.caffemodel
I1025 19:30:29.283386 31228 solver.cpp:315] Iteration 2500, loss = 3.91634
I1025 19:30:29.283444 31228 solver.cpp:320] Optimization Done.
I1025 19:30:29.283535 31230 solver.cpp:315] Iteration 2500, loss = 3.9612
I1025 19:30:29.283582 31230 solver.cpp:320] Optimization Done.
I1025 19:30:29.285512 31228 caffe.cpp:357] Optimization Done.
I1025 19:30:29.285521 31228 caffe.cpp:359] [MPI:1   ] MPI_Finalize
I1025 19:30:29.285697 31230 caffe.cpp:357] Optimization Done.
I1025 19:30:29.285706 31230 caffe.cpp:359] [MPI:3   ] MPI_Finalize
I1025 19:30:29.286912 31229 solver.cpp:315] Iteration 2500, loss = 3.90313
I1025 19:30:29.286952 31229 solver.cpp:320] Optimization Done.
I1025 19:30:29.290489 31229 caffe.cpp:357] Optimization Done.
I1025 19:30:29.290498 31229 caffe.cpp:359] [MPI:2   ] MPI_Finalize
I1025 19:30:29.973234 31227 sgd_solver.cpp:356] Snapshotting solver state to binary proto file models/bvlc_alexnet/caffe_alexnet_train_iter_2500.solverstate
I1025 19:30:30.727695 31227 solver.cpp:315] Iteration 2500, loss = 3.89638
I1025 19:30:30.727744 31227 solver.cpp:320] Optimization Done.
I1025 19:30:30.729465 31227 caffe.cpp:357] Optimization Done.
I1025 19:30:30.729475 31227 caffe.cpp:359] [MPI:0   ] MPI_Finalize



2017년 7월 24일 월요일

Minsky 서버에서 ImageNet Contest 2012 data로 Caffe AlexNet training 해보기

# 먼저, ILSVRC2012 data를 고성능 filesystem(NVMe SSD 또는 ESS 등)에 download 받습니다

b6p318za@p10a109:~/nvme/ilsvrc2012$ wget http://www.image-net.org/challenges/LSVRC/2012/nnoupb/ILSVRC2012_img_train.tar  # 138GB
b6p318za@p10a109:~/nvme/ilsvrc2012$ wget http://www.image-net.org/challenges/LSVRC/2012/nnoupb/ILSVRC2012_img_train_t3.tar  # 728MB
b6p318za@p10a109:~/nvme/ilsvrc2012$ wget http://www.image-net.org/challenges/LSVRC/2012/nnoupb/ILSVRC2012_img_val.tar   #  6.3GB
b6p318za@p10a109:~/nvme/ilsvrc2012$ wget http://www.image-net.org/challenges/LSVRC/2012/nnoupb/ILSVRC2012_img_test.tar  # 13GB

b6p318za@p10a109:/nvme/ilsvrc2012$ ls -ltr
total 165357316
-rw-r----- 1 b6p318za IBM1   6744924160 Jun 14  2012 ILSVRC2012_img_val.tar
-rw-r----- 1 b6p318za IBM1 147897477120 Jun 14  2012 ILSVRC2012_img_train.tar
-rw-r----- 1 b6p318za IBM1    762460160 Jul  4  2012 ILSVRC2012_img_train_t3.tar
-rw-r----- 1 b6p318za IBM1  13685811200 Jul  9  2012 ILSVRC2012_img_test.tar

# 이렇게 download 받은 image tar file들을 각각의 directory에 풀어놓습니다.  단, train용 image tar file을 풀면 그 속에 다시 1000개의 tar file이 나오니, 그것들을 또 한번 더 풀어주어야 합니다.

b6p318za@p10a109:/nvme/ilsvrc2012$ mkdir val
b6p318za@p10a109:/nvme/ilsvrc2012$ mkdir test
b6p318za@p10a109:/nvme/ilsvrc2012$ mkdir train
b6p318za@p10a109:/nvme/ilsvrc2012$ mkdir train_t3

b6p318za@p10a109:/nvme/ilsvrc2012$ cd val
b6p318za@p10a109:/nvme/ilsvrc2012/val$ tar -xvf ../ILSVRC2012_img_val.tar

b6p318za@p10a109:/nvme/ilsvrc2012$ cd train
b6p318za@p10a109:/nvme/ilsvrc2012/train$ tar -xvf ../ILSVRC2012_img_train.tar
b6p318za@p10a109:/nvme/ilsvrc2012/train$ for i in `ls`
> do
> dir=`echo $i | cut -d. -f1`
> mkdir $dir
> cd $dir
> tar -xf ../$i
> cd ..
> done

b6p318za@p10a109:/nvme/ilsvrc2012$ cd test
b6p318za@p10a109:/nvme/ilsvrc2012/test$ tar -xvf ../ILSVRC2012_img_test.tar

b6p318za@p10a109:/nvme/ilsvrc2012$ cd train_t3
b6p318za@p10a109:/nvme/ilsvrc2012/train_t3$ tar -xvf ../ILSVRC2012_img_train_t3.tar
b6p318za@p10a109:/nvme/ilsvrc2012/train_t3$ for i in *.tar
> do
> tar -xvf $i
> done


# 기본 시스템 성능 튜닝입니다.   GPU의 autoboost도 on 시켜 놓습니다.

b6p318za@p10a109:~$ sudo apt-get install linux-tools-common cpufrequtils
b6p318za@p10a109:~$ sudo cpupower frequency-set --governor performance

b6p318za@p10a109:~$ sudo nvidia-smi -pm ENABLED

b6p318za@p10a109:~$ sudo nvidia-smi -ac 715,1480


# 이미 설치된 PowerAI 중 NV-caffe를 사용합니다.  이를 위해서는 아래와 같은 명령을 수행하여 PATH 환경 변수 등을 NV-caffe를 기본으로 하도록 합니다.   PowerAI 설치에 대해서는 지난 posting ( http://hwengineer.blogspot.kr/2017/05/minsky-cuda-powerai-tuning.html )을 참조하십시요.

b6p318za@p10a109:/opt/DL$ source /opt/DL/caffe-nv/bin/caffe-activate

b6p318za@p10a109:/opt/DL$ which caffe
/opt/DL/caffe-ibm/bin/caffe


# 아래와 같이 caffe-test를 수행했을 때 혹시 error가 난다면 다음과 같이 LD_LIBRARY_PATH를 제대로 설정해주시면 됩니다.

b6p318za@p10a109:/opt/DL$ caffe-test
caffe-test: symbol lookup error: /opt/DL/caffe-ibm/test/../lib/libcaffe.so.1.0.0-rc3: undefined symbol: _ZNK6google8protobuf7Message11GetTypeNameB5cxx11Ev

b6p318za@p10a109:~$ export LD_LIBRARY_PATH=/usr/lib/powerpc64le-linux-gnu:/usr/local/lib:$LD_LIBRARY_PATH

b6p318za@p10a109:~$ sudo vi /etc/ld.so.conf.d/DL.conf
/opt/DL/openblas/lib

b6p318za@p10a109:~$ sudo ldconfig

b6p318za@p10a109:/opt/DL$ caffe-test
...
[----------] Global test environment tear-down
[==========] 2081 tests from 277 test cases ran. (664293 ms total)
[  PASSED  ] 2081 tests.


# Alexnet 수행 준비를 위해, 먼저 get_ilsvrc_aux.sh를 수행합니다.  그리고 download된 train.txt의 내용을 실제 file 위치에 맞도록 수정합니다.

b6p318za@p10a109:/opt/DL/caffe-ibm/data/ilsvrc12$ ./get_ilsvrc_aux.sh

b6p318za@p10a109:/opt/DL/caffe-ibm$ vi data/ilsvrc12/train.txt
n01440764/n01440764_10026.JPEG 0
n01440764/n01440764_10027.JPEG 0
...
---->
/nvme/ilsvrc2012/train/n01440764/n01440764_10026.JPEG 0
/nvme/ilsvrc2012/train/n01440764/n01440764_10027.JPEG 0
...

# 전체 image 준비를 위해 create_imagenet.sh를 수행합니다.  단, 수행 전에 이 script에서 RESIZE=true로 바꾸고, 기타 TRAIN_DATA_ROOT 등의 각종 path 등을 수정합니다.   특히, 기본적으로 $EXAMPLE 밑에 생성되도록 되어 있는 ilsvrc12_train_lmdb과 ilsvrc12_val_lmdb 이 고성능 filesystem(NVMe 혹은 ESS)에 생성되도록 path를 바꿔 줍니다.

b6p318za@p10a109:/opt/DL/caffe-ibm$ vi ./examples/imagenet/create_imagenet.sh
...
RESIZE=true
#RESIZE=false

b6p318za@p10a109:/opt/DL/caffe-ibm$ time ./examples/imagenet/create_imagenet.sh
Creating train lmdb...
...
I1116 08:56:31.806813  8234 convert_imageset.cpp:147] Processed 49000 files.
I1116 08:56:37.932953  8234 convert_imageset.cpp:147] Processed 50000 files.
Done.

(약 2시간 넘게 걸립니다.)

# 그 결과로 LMDB file들이 제대로 생성되었는지 확인합니다.

b6p318za@p10a109:/opt/DL/caffe-ibm$ ls -l /nvme/ilsvrc2012/ilsvrc12_train_lmdb
total 287040648
-rw-r----- 1 b6p318za IBM1 293929582592 Nov 16 08:51 data.mdb
-rw-r----- 1 b6p318za IBM1         8192 Nov 16 08:59 lock.mdb
b6p318za@p10a109:/opt/DL/caffe-ibm$ ls -l /nvme/ilsvrc2012/ilsvrc12_val_lmdb
total 11203084
-rw-r----- 1 b6p318za IBM1 11471945728 Nov 16 08:56 data.mdb
-rw-r----- 1 b6p318za IBM1        8192 Nov 16 08:56 lock.mdb


# 생성된 LMDB로부터 전체 imagenet data의 평균값을 구하기 위해 make_imagenet_mean.sh를 수행합니다.  (약 6시간 가까이 걸립니다.)

b6p318za@p10a109:/opt/DL/caffe-ibm$ nohup time ./examples/imagenet/make_imagenet_mean.sh &



# models/bvlc_alexnet/solver.prototxt을 적절한 위치로 copy 한 뒤 수정하여 stepsize를 20000으로 바꾸고, 각종 path도 적절한 위치로 바꿉니다.

b6p318za@p10a109:/tmp$ vi /nvme/bsyu/solver.prototxt
net: "/nvme/bsyu/train_val.prototxt"
test_iter: 1000
test_interval: 1000
base_lr: 0.01
lr_policy: "step"
gamma: 0.1
stepsize: 20000
display: 20
max_iter: 25000
momentum: 0.9
weight_decay: 0.0005
snapshot: 25000
snapshot_prefix: "/nvme/bsyu/models/caffe_alexnet_train"
solver_mode: GPU

# models/bvlc_alexnet/train_val.prototxt를 적절한 위치로 copy 한 뒤 수정하여 train data의 batch_size를 256으로, val data의 batch_size는 64로 바꾸고, 각종 path도 적절히 변경합니다.

b6p318za@p10a109:/tmp$ vi "/nvme/bsyu/train_val.prototxt"
name: "AlexNet"
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
    mean_file: "/nvme/ilsvrc2012/imagenet_mean.binaryproto"
  }
  data_param {
    source: "/nvme/ilsvrc2012/ilsvrc12_train_lmdb"
    batch_size: 256
    backend: LMDB
  }
}
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "/nvme/ilsvrc2012/imagenet_mean.binaryproto"
  }
  data_param {
    source: "/nvme/ilsvrc2012/ilsvrc12_val_lmdb"
    batch_size: 64
    backend: LMDB
  }
}
...


# 다음과 같은 train_alexnet.sh를 만들어 수행합니다.

b6p318za@p10a109:/opt/DL/caffe-nv$ vi ./examples/imagenet/train_alexnet.sh
#!/usr/bin/env sh
set -e
#./bin/caffe train -gpu 0,1 --solver=models/bvlc_alexnet/solver.prototxt
./bin/caffe train -gpu all --solver=models/bvlc_alexnet/solver.prototxt


b6p318za@p10a109:/opt/DL/caffe-nv$ nohup time ./examples/imagenet/train_caffenet.sh &
[1] 9820


# 결과로 생기는 nohup.out에서 grep accuracy를 수행하여, accuracy가 0.5 이상으로 올라갈 때까지 걸린 시간을 계산합니다.

b6p318za@p10a109:/opt/DL/caffe-ibm$ grep accuracy ~/nohup.out
I1118 06:55:19.538242 48597 solver.cpp:442]     Test net output #0: accuracy = 0.000999999
I1118 07:41:13.278080 48597 solver.cpp:442]     Test net output #0: accuracy = 0.18552
I1118 07:50:19.552739 48597 solver.cpp:442]     Test net output #0: accuracy = 0.26228
...
I1118 08:27:07.605918 48597 solver.cpp:442]     Test net output #0: accuracy = 0.405379
I1118 08:36:16.722012 48597 solver.cpp:442]     Test net output #0: accuracy = 0.42512
...
I1118 11:36:23.555583 48597 solver.cpp:442]     Test net output #0: accuracy = 0.50274
I1118 20:38:10.207644 48597 solver.cpp:442]     Test net output #0: accuracy = 0.52026

(약 1시간 남짓 걸릴 것입니다.)