まんぼう日記

takataka's diary

大石東バイパスとTheanoでGPGPU

なんでまたそういう無関係なネタを一つの記事にするっ,ておしかりをうけそーですが (^^;  前者は小ネタなので…

 

国道422号線大石東バイパスの話

の続きのようなそうでもないような話.瀬田川にかける橋の方は完成までまだ何年もかかるということやったけど,あっちのトンネルの方は7月末までってことになってたよなー,とか思い出して,ウェブで検索してみたら,こんなん見つけました.

http://www.pref.shiga.lg.jp/h/d-kanri/kikaku/files/2_uehara.pdf

ルート計画の話とか,橋種選定の話(図4第3案の鋼斜張橋がヘンテコですてき)とか,橋梁工事の工程(図6)とか,面白いことがいっぱいです.図7の今後の事業工程によると,橋の上部工は今年度第4四半期から,トンネル(関津トンネルというらしい)は来年度第4四半期から工事が始まる予定となってました.7月末までってのはトンネルまでの道路部分の工事のことなんでしょうね.計画ではトンネルも橋も平成30年度末開通ってことになってます.今からそこをてくてくする日が待ち遠しい (^^)

 

TheanoでGPGPUの話

 昨日無事に GPGPU 用マシンの設定ができた( 続 GPGPU用マシンにCUDAをインストール - まんぼう日記 )ので,早速ニューラルネットの学習の実験をやってみました.ハード/ソフトの環境についてはリンクを辿ってください.

 

プログラムの作成というか修正

以前,Theanoで多層パーセプトロン(MLP)の学習をするプログラムを作って,MNIST の手書き数字認識の実験をやったことがあります( cf.  Theano で MLP & CNN (2) - まんぼう日記 ).今回は,その時のプログラムをちょこっと修正して GPU 上で動かしてみることにしました.

 

GPUは倍精度(64bit)浮動小数点数の計算が苦手で,単精度(32bit)の方がよいみたいなので,まずは theano.tensor の float64 な型を使ってる箇所を修正して,float32 / float64 の切り替えが簡単にできるようにしました.上記の記事のリンク先にある nnet0211.py で T.dmatrix / T.dvector / T.dscalar にしてたところを,T.matrix / T.vector / T.scalar に.これで,theano.config.floatX の値を指定( 'float32' または 'float64',デフォルトは 'float64' )するだけで切り替えられます.直にプログラム中に theano.config.floatX = 'float32' のように書いてもよいし,環境変数 THEANO_FLAGS で指定することも可能(詳細はこちら: config – Theano Configuration — Theano 0.7 documentation.この記事の下の方にも環境変数で指定してる例があります).

 

それだけ修正したらokかと思いましたが,実行してみたら

TypeError: ('Bad input argument to theano function with name "/home/takataka/
.../nnet150612.py:95" at index 0(0-based)', 'TensorType(float32, matrix)
cannot store a value of dtype float64 without risking loss of precision.
If you do not mind this loss, you can: 1) explicitly cast your data
to float32, or 2) set "allow_input_downcast=True" when calling "function".',
array([[ 0.10094242,  0.09894396,  0.10255994, ...,  0.09911078,

というようなエラーが出てきました.デフォルトでは float32 な theano.tensor に float64 な値を代入させてくれへんのですね.ということは,numpy.array にデータを格納してる所も修正して,単精度浮動小数点数の配列にしとけばよいんでしょう.というわけでちょこちょこ修正.

 

今度こそokかと思ったら,まだエラーが.そっか,shared variable のデータ型もあわせなあきませんな.Numpy で作ったゼロ行列やら乱数を theano.shared に渡してたとこも修正.

 

三度目の正直,これでどや…動きました  \(^^)/

 

プログラムはこちら:

 

実験結果

さあ実験.詳しい実験条件等については…省略.以下の出力とかプログラムとか,Theano で MLP & CNN (2) - まんぼう日記 やそのリンク先の記事を参照してください.

 

まずは,「入力 - 隠れ層 - 出力層」という2層のMLP. 

 

2層MLP,CPUで実行
$ time THEANO_FLAGS='device=cpu,floatX=float32' python mnist_mlp150612.py 
### 2-layer MLP: D = 784  H (ReLu) = 500  K (softmax) = 10
### training:   NL =  50000 NV =  10000  batchsize =  100
#               eta =  0.01 mu =  0.9 lam =  1e-05
0 | 2.3045 93.27 | 2.3044 93.48
10 | 0.0794 2.11 | 0.0976 2.69
20 | 0.0366 0.79 | 0.0733 2.11
30 | 0.0205 0.28 | 0.0678 1.99
40 | 0.0127 0.13 | 0.0661 1.91
50 | 0.0085 0.04 | 0.0663 1.86
# NT =  10000
50 | 0.0085 0.04 | 0.0663 1.86 | 0.0645 1.94

real    2m30.410s
user    10m41.883s
sys    18m25.275s

実際の経過時間(real)よりユーザCPU時間(user)の方が長くなってるのは,マルチコアで並列計算してるから.実コア数6,Hyper Threadingで仮想コア数12のCPUなので,理想的には12倍までいきます.

 

2層MLP,GPUで実行
$ time THEANO_FLAGS='device=gpu1,floatX=float32,nvcc.fastmath=True' python mnist_mlp150612.py 
Using gpu device 1: Quadro K620
(中略)
50 | 0.0085 0.04 | 0.0661 1.88 | 0.0645 1.95

real    3m10.979s
user    2m41.005s
sys    0m30.319s
$ time THEANO_FLAGS='device=gpu0,floatX=float32,nvcc.fastmath=True' python mnist_mlp150612.py 
Using gpu device 0: Tesla K20c
50 | 0.0085 0.04 | 0.0661 1.87 | 0.0644 1.92
(中略)
real    1m18.779s
user    0m58.470s
sys    0m20.841s

上が Quadro の場合,下が Tesla の場合.Quadro ではCPUに負けてますが,さすがに Tesla は速いです.今回のプログラムでは,SGDのミニバッチ毎にメインメモリとGPUの間でデータ転送が発生するのですが,これくらいの規模のネットワークでは,GPU内でのネットワーク出力の計算&誤差逆伝播学習の計算よりもそっちのオーバーヘッドの影響の方が大きいのかもしれません.

 

ちなみに,nvcc.fastmath = True というオプションも渡してますが,今回の実験ではなしでも計算時間は変わらず,その効果のほどはわかりませんでした.

 

次は,隠れ層をもう一つ入れて,「入力 - 隠れ層 - 隠れ層 - 出力層」という3層のMLPで. 

 

3層MLP,CPUで実行
$ time THEANO_FLAGS='device=cpu,floatX=float32' python mnist_mlp150612.py 
### 3-layer MLP: D = 784  H1 (ReLu) = 1000  H2 (ReLu) = 500  K (softmax) = 10
### training:   NL =  50000 NV =  10000  batchsize =  100
#               eta =  0.01 mu =  0.9 lam =  1e-05
0 | 2.3030 92.28 | 2.3028 92.61
10 | 0.0303 0.68 | 0.0747 2.18
20 | 0.0058 0.03 | 0.0716 1.88
30 | 0.0024 0.00 | 0.0761 1.87
40 | 0.0013 0.00 | 0.0787 1.81
50 | 0.0010 0.00 | 0.0809 1.82
# NT =  10000
50 | 0.0010 0.00 | 0.0809 1.82 | 0.0778 1.82

real    6m21.194s
user    34m25.742s
sys    40m42.999s

3層にしたことで,userは11分→34.5分,realは2.5分→6分となりました.

 

ついでに,float32 と float64 の比較も.

$ time THEANO_FLAGS='device=cpu,floatX=float64' python mnist_mlp150612.py
(中略)
50 | 0.0010 0.00 | 0.0811 1.80 | 0.0778 1.86

real    9m21.587s
user    62m26.379s
sys    47m39.997s

1.5倍ってとこですね.

 

3層MLP,GPUで実行
$ time THEANO_FLAGS='device=gpu1,floatX=float32,nvcc.fastmath=True' python mnist_mlp150612.py 
Using gpu device 1: Quadro K620
(中略)
50 | 0.0010 0.00 | 0.0810 1.79 | 0.0778 1.87

real    3m38.336s
user    2m53.285s
sys    0m45.223s
$ time THEANO_FLAGS='device=gpu0,floatX=float32,nvcc.fastmath=True' python mnist_mlp150612.py 
Using gpu device 0: Tesla K20c
(中略)
50 | 0.0010 0.00 | 0.0808 1.82 | 0.0777 1.84

real    1m54.144s
user    1m21.258s
sys    0m33.203s

3層MLPの方は,Quadro でも CPU に勝ちました.しかも,2層の時とあまり所要時間が変わってません.メモリ転送のオーバーヘッドが全体に占める割合は2層の時より減ってはいるものの,まだそれが大きくて,GPUが性能を出し切れてないってことかもしれませんね.

 

ちなみに,Tesla で実行中に nvidia-smi コマンドでGPUのメモリ使用量を表示させてみたら,上記プログラムのプロセスが使ってるメモリは 90MiB でした.

 

それから,Tesla の方で float64 でも実験.

$ time THEANO_FLAGS='device=gpu0,floatX=float64' python mnist_mlp150612.py
Using gpu device 0: Tesla K20c
50 | 0.0010 0.00 | 0.0811 1.80 | 0.0778 1.86

real	9m17.319s
user	62m35.160s
sys	47m33.556s

なんてこった.えらい遅くなりよう…さらにユーザCPU時間の変化がおかしなことに.CPU使った時とほとんど同じになってます.なんか変….

 

とりあえず今日はこんなところで.