Speee Advent Calendar 2017の5日目の記事です。
4日は僕からGoとポモドーロなGomodoroを一ヶ月運用してみてでした!
はじめに
今回はプライベートで参加しているRed Data ToolsというRuby用のデータ処理ツールを提供するためのプロジェクトで作っているRed Chainerというgemの実行速度をコードを変更せずに約2倍くらい早くするネタ。
Red Chainerとは?
一言で言うとRubyで書かれた深層学習フレームワークです!
github.com
PythonにはChainerと呼ばれる日本製の深層学習フレームワークがあるのですが、これをRubyへポーティングしたものがこのRed Chainerです。
※ ポーティングは気合で手でやってます
Rubyで1から書いていて一部でPythonを外部実行してるみたいなことはしていないです。
ファーストリリースを10月13日に行いまだまだ発展途上のgemです👍
Red Chainerの実行速度について
まずRed Chainerは内部での行列の扱いにNumo::NArrayというgemを使用しています。
このgemの作者の方がRubyKaigi2017で発表されていたProgress of Ruby/Numo: Numerical Computing for Rubyに今回のネタになることが書いてありました。
それが次のスライドです。
ざっくり書くと1コアを使うNumo::NArrayはOpenBLASで4コア使えるNumpyと比べると遅いけどLinalgを使うとOpenBLASが使えるようになって4コア使えるようになるから使うとスピードあがるよ!といった内容(のはず)。
これはやってみる価値がありそうなのでさっそくやってみる!
その前にOpenBLAS? Linalg?
OpenBLASはBLASと呼ばれるベクトルと行列に関する線型代数操作を実行するAPIを提供するものでOpenBLASはそれをオープンソースで提供しているものです。他にもATLASといったものがあるのですがruby-numo/linalgはこのBindingを提供してくれます。
速度比較
今回は誰でも試せるようにDocker Imageにしてそれを使って検証しました。
docker image, Dockerfile
動作環境はEC2インスタンスでvCPU2のt2.medium上でDockerでやってます。
Native Implement
まずは通常のNumo::NArrayで検証した場合。
$ docker run -it hatappi/red-chainer ruby /opt/examples/mnist.rb Downloading from http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time 1 0.190656 0.943517 506.072 2 0.0726881 0.977267 1013.04 3 0.048782 0.984417 1519.71 4 0.0380302 0.987617 2025.57 5 0.0276014 0.991033 2532.41 6 0.0238486 0.992383 3038.69 7 0.0217516 0.99325 3544.98 8 0.0180251 0.9942 4051.35 9 0.0175256 0.994517 4558.07 10 0.0158116 0.995183 5107.94 11 0.012952 0.996133 6240.86 12 0.0133668 0.995667 7553.72 13 0.00987872 0.996933 8875.83 14 0.0132002 0.99605 10177 15 0.0123773 0.996183 11505.8 16 0.00862055 0.9976 12860 17 0.00997421 0.997067 14178 18 0.00980791 0.99715 15475.7 19 0.0108061 0.997033 16781.6 20 0.0109783 0.996983 18122
約302分・・・・
OpenBLAS with 2cores by loading Linalg
こちらが今回検証対象のOpenBLASのバインディングを提供するruby-numo/linalgをrequireした場合
[root@ip-172-10-2-213 ec2-user]# time docker run -it hatappi/red-chainer ruby -r numo/linalg/use/openblas /opt/examples/mnist.rb Downloading from http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz Downloading from http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time 1 0.18923 0.942783 127.257 2 0.0760872 0.97645 254.426 3 0.0481313 0.984517 383.101 4 0.0352307 0.988617 511.491 5 0.0284791 0.99045 639.283 6 0.0246408 0.992033 766.936 7 0.0213544 0.992983 975.04 8 0.0195042 0.9936 1515.91 9 0.0166227 0.99455 2227.03 10 0.0164481 0.994817 2889.97 11 0.0133394 0.996 3603.04 12 0.0111049 0.996417 4275.72 13 0.0110981 0.996683 4992.94 14 0.0154447 0.9956 5710.56 15 0.00854361 0.997267 6374.21 16 0.00981744 0.997117 7084.47 17 0.00985091 0.997233 7801.86 18 0.00997248 0.996917 8477.44 19 0.00844054 0.9975 9199.59 20 0.00938205 0.997467 9923.02
約165分!
半分近くまで早くなってる!
ちなみにPythonのChainer
docker run -it hatappi/chainer python3 mnist.py GPU: -1 # unit: 1000 # Minibatch-size: 100 # epoch: 20 Downloading from http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz... Downloading from http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz... Downloading from http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz... Downloading from http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz... epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time 1 0.193236 0.08251 0.941117 0.9737 18.3165 2 0.073018 0.0945126 0.977233 0.9683 38.1975 3 0.0478583 0.0625547 0.984767 0.9812 58.7991 4 0.0350756 0.0776801 0.989383 0.9789 80.2456 5 0.0286255 0.0929291 0.9908 0.9766 101.535 6 0.0259772 0.0842944 0.991667 0.9769 123.005 7 0.0191559 0.0861707 0.993383 0.9808 144.731 8 0.0181119 0.085485 0.9938 0.982 166.782 9 0.017674 0.0797785 0.9946 0.9811 189.257 10 0.0142222 0.0771581 0.9955 0.9818 212.275 11 0.014423 0.0700368 0.995717 0.9829 235.738 12 0.0109772 0.0925245 0.996467 0.9812 259.602 13 0.0151957 0.0870395 0.995233 0.9792 283.742 14 0.00829544 0.0934487 0.9972 0.9807 308.831 15 0.0117414 0.08807 0.996567 0.9824 334.308 16 0.0101929 0.0885494 0.9968 0.9831 360.316 17 0.00932984 0.119531 0.9971 0.9792 386.981 18 0.0129252 0.0968258 0.996333 0.9827 414.402 19 0.00378327 0.088825 0.999183 0.9841 442.64 20 0.0116622 0.107006 0.996833 0.9813 471.083
7分w
結果
検証方法 | 実行時間 |
---|---|
Numo::NArray | 302分 |
Numo::NArray OpenBLAS with 2cores by loading Linalg |
165分 |
Python Chainer | 7分 |
Pythonの実装と比べるとまだまだではあるけど、Numo::NArrayだけで見ると半分くらいまでは短くなっていた :tada:
最後に
今回はコードを書き換えなくても実行時間が短くなる方法を試しました。
Red Chainerにまだまだ改良できるところはあるので、これからコツコツ改善していきたい!
明日はyhattが何かを書くそうです!
お楽しみに!