Ubuntu14.04にChainer + CUDAの環境をセットアップする
多分、この通りにやるとchainerがgpuで動作するまで行けます。
sudo su apt-get -y update apt-get -y upgrade apt-get -y install gcc fortran python-dev apt-get -y install libjpeg-dev libfreetype6 libfreetype6-dev zlib1g-dev apt-get -y install libjpeg-dev zlib1g-dev libpng12-dev apt-get -y install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose apt-get install nvidia-346-updates
CUDA環境をインストール
NVIDIAからdeb(network)をダウンロード先のアドレスをコピー
wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/cuda-repo-ubuntu1404_7.5-18_amd64.deb dpkg -i cuda-repo-ubuntu1404_7.5-18_amd64.deb apt-get update apt-get install cuda
/root/.bashrc の最後に追加
export PATH=/usr/local/cuda-7.5/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-7.5/lib64:$LD_LIBRARY_PATH
変更を反映
source .bashrc
chainerをインストール
curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python pip install numpy --upgrade ## pip install scipy --upgrade pip install chainer pip uninstall pycuda scikit-cuda chainer-cuda-deps pip install chainer-cuda-deps --no-cache-dir pip install Pillow --upgrade exit
~/.bashrc の最後に追加
export PATH=/usr/local/cuda-7.5/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-7.5/lib64:$LD_LIBRARY_PATH
できあがり。
ニューラルネットワークを逆に辿って文字コードから画像を出せるか?
Chainerのサンプルプログラム MNISTは数字の画像から10種類の数字コード?を出力する。
これを逆にして、数字コードから数字の画像を出力できないだろうか?
## MNISTのコードに追加する model.to_cpu() a = np.linalg.pinv( model.l3.W ) b = np.linalg.pinv( model.l2.W ) c = np.linalg.pinv( model.l1.W ) d = np.dot(b,a) e = np.dot(c,d) for ii in xrange(10): f = np.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],np.float32) f[ii] = 1.0 g = np.dot(e,f).reshape(28,28) g = g / np.linalg.norm(g) img = Image.new('RGB',(28,28) ) for y in xrange(28): for x in xrange(28): v = g[x][y] if v>1.0: v=1.0 if v<0: v=0.0 vv = int( v*255 ) img.putpixel( (x,y) ,(vv,vv,vv) ) img.save("out"+str(ii)+".png",'PNG')
ものすごくテキトーなコードだけど、これでどうなんでしょうか?
行列の代わりに逆行列(pseudo-inverse matrix)を使って計算しています。
でもこれだと結局ノイズみたいな画像しか出てこなくて失敗っぽいです。
もし上手く行くんだったら教えて欲しいです。
漢字を読むことはできるけど書くことができないってことよくありますよね?
読む回路と書く回路は別に作る必要があるんじゃないかって気がしました。
ChainerでNVIDIAのcuDNNを使えるようにする
ChainerでCUDAを使えるようにしていたけど、cuDNNは自分で入れないといけないらしい。知らなかった!
NVIDIA cuDNN – GPU Accelerated Deep Learning
ここからダウンロードするんだけど、その際CUDA Registered Developer Programとして登録しなければならない。ちょっと面倒くさい。
後でメールが送られてきて本登録されるとやっとダウンロードできる。
ドキュメントに書いてある方法とは違うけど、Ubuntu14.04では、
- ヘッダーは /usr/local/cuda-7.0/include/ にコピー
- ライブラリは /usr/local/cuda-7.0/lib64/ にコピー
で動くようになった。
効果のほどは
cuDNNなし 2m0.367s
cuDNNあり 1m50.148s
約10%のスピードアップ。ライブラリーを入れるだけで10%速くなるのは大きいよ!
ChainerのMNISTをConvolution2Dに改造する
Convolution2Dの使い方がよくわかんない。
よくわかんないのでとりあえず動作しているサンプルを改造して動きを見てみることにした。
#!/usr/bin/env python """Chainer example: train a multi-layer perceptron on MNIST This is a minimal example to write a feed-forward net. It requires scikit-learn to load MNIST dataset. """ import argparse import numpy as np import six import chainer from chainer import computational_graph as c from chainer import cuda import chainer.functions as F from chainer import optimizers import data parser = argparse.ArgumentParser(description='Chainer example: MNIST') parser.add_argument('--gpu', '-g', default=-1, type=int, help='GPU ID (negative value indicates CPU)') args = parser.parse_args() if args.gpu >= 0: cuda.check_cuda_available() xp = cuda.cupy if args.gpu >= 0 else np batchsize = 100 n_epoch = 20 # Prepare dataset print('load MNIST dataset') mnist = data.load_mnist_data() mnist['data'] = mnist['data'].astype(np.float32) mnist['data'] /= 255 mnist['data'] = mnist['data'].reshape(70000, 1,28,28) mnist['target'] = mnist['target'].astype(np.int32) N = 60000 x_train, x_test = np.split(mnist['data'], [N]) y_train, y_test = np.split(mnist['target'], [N]) N_test = y_test.size # Prepare multi-layer perceptron model model = chainer.FunctionSet(cv1=F.Convolution2D(1,30, 3), bn2 = F.BatchNormalization( 30), ln3=F.Linear(5070, 1000), ln4=F.Linear(1000, 10)) if args.gpu >= 0: cuda.get_device(args.gpu).use() model.to_gpu() def forward(x_data, y_data, train=True): # Neural net architecture x, t = chainer.Variable(x_data), chainer.Variable(y_data) h = F.max_pooling_2d(F.dropout(F.relu(model.bn2(model.cv1(x))), train=train),2) h = F.dropout(F.relu(model.ln3(h)), train=train) y = model.ln4(h) return F.softmax_cross_entropy(y, t), F.accuracy(y, t) # Setup optimizer optimizer = optimizers.Adam() optimizer.setup(model) # Learning loop for epoch in six.moves.range(1, n_epoch + 1): print('epoch', epoch) # training perm = np.random.permutation(N) sum_accuracy = 0 sum_loss = 0 for i in six.moves.range(0, N, batchsize): x_batch = xp.asarray(x_train[perm[i:i + batchsize]]) y_batch = xp.asarray(y_train[perm[i:i + batchsize]]) optimizer.zero_grads() loss, acc = forward(x_batch, y_batch) loss.backward() optimizer.update() if epoch == 1 and i == 0: with open("graph.dot", "w") as o: o.write(c.build_computational_graph((loss, )).dump()) with open("graph.wo_split.dot", "w") as o: g = c.build_computational_graph((loss, ), remove_split=True) o.write(g.dump()) print('graph generated') sum_loss += float(loss.data) * len(y_batch) sum_accuracy += float(acc.data) * len(y_batch) print('train mean loss={}, accuracy={}'.format( sum_loss / N, sum_accuracy / N)) # evaluation sum_accuracy = 0 sum_loss = 0 for i in six.moves.range(0, N_test, batchsize): x_batch = xp.asarray(x_test[i:i + batchsize]) y_batch = xp.asarray(y_test[i:i + batchsize]) loss, acc = forward(x_batch, y_batch, train=False) sum_loss += float(loss.data) * len(y_batch) sum_accuracy += float(acc.data) * len(y_batch) print('test mean loss={}, accuracy={}'.format( sum_loss / N_test, sum_accuracy / N_test))
解説
Convolution2Dに渡せるように配列、行列の形を変えます。
サンプル数70000個、チャンネル数1、縦横28*28
mnist['data'] = mnist['data'].reshape(70000, 1,28,28)
ニューラルネットワークに使う部品
# Prepare multi-layer perceptron model model = chainer.FunctionSet(cv1=F.Convolution2D(1,30, 3), bn2 = F.BatchNormalization( 30), ln3=F.Linear(5070, 1000), ln4=F.Linear(1000, 10))
ニューラルネットワークの定義
def forward(x_data, y_data, train=True): # Neural net architecture x, t = chainer.Variable(x_data), chainer.Variable(y_data) h = F.max_pooling_2d(F.dropout(F.relu(model.bn2(model.cv1(x))), train=train),2) h = F.dropout(F.relu(model.ln3(h)), train=train) y = model.ln4(h) return F.softmax_cross_entropy(y, t), F.accuracy(y, t)
変わっているのはこの辺だけです。
Convolution2DからLinearにつなげる時にサイズがわからないけど、とりあえず実行してみて
chainer.utils.type_check.InvalidType: Expect: prod(in_types[0].shape[1:]) == W.shape[1] Actual: 1690 != 6760
エラーを見て数値を変更すればおk。
Numpyのndarrayを使ったPythonのコードをCythonで高速化する
私がPythonに入門したその日に行ったのは、「C や C++ による Python の拡張」だ。MNISTのサンプルを改造して画像を8bitパソコンみたいに変換するようにしてみたのだが、とにかく遅い。
速さは力だ!トライアンドエラーをたくさん繰り返して学ぶのは人間もニューラルネットワークも同じだ。スピードが遅ければそれだけ学習も遅くなる。だからとにかく速くしたい!
ErlangのNIFでC言語を使って高速化するのには慣れているのでPythonでもやってみた。でもあまり速くならなかった。
Pythonで高速化するにはもっとPythonの作法を知らなければならない。配列とかnumpyのことをよく知らずに小手先だけ高速化してもほとんど効果はないのだ。
さて、Pillowで読み込んだイメージからタイルをnumpyのarrayにどんどん切り出していく初めて書いたコードがこれだ。
def get_tiles2c(im,ts2,off): wlen = im.width buf = np.array(im.getdata(),np.float32).reshape( im.width*im.height*Components ) / 255.0 for ty in xrange(Margin,im.height -Margin): tiles = [] for tx in xrange(Margin,im.width -Margin): list = array.array('f') for y in xrange(ts2): for x in xrange(ts2): idx = ( x+tx-Margin + ( y+ty-Margin ) * wlen ) * Components list.append( buf[ idx ] ) tiles.append( np.frombuffer(list,dtype=np.float32) ) yield tiles yield []
うそです。最初はもっとひどいコードでした。いろいろいじくりまわしてこれでも相当高速化したつもりです。
# 302.02212405204773秒
これをCythonで高速化していく。CythonはCPythonの書き間違いじゃないよ。
なんと、CythonはPythonのソースコードをC言語にコンバートたりPythonから読み込めるDLL,SOに変換してくれるのだ!!
このコードを何も考えずにCythonでコンパイルしてみると
# 274.2430958747864秒
少し速くなった!
これに型情報をつけて高速化していく
@cython.boundscheck(False) @cython.wraparound(False) def get_tiles2(im,ts2,off): cdef int tx,ty cdef int x,y cdef int wlen cdef int idx cdef np.ndarray buf wlen = im.width buf = np.array(im.getdata(),np.float32).reshape( im.width*im.height*Components ) / 255.0 for ty in xrange(Margin,im.height -Margin): tiles = [] for tx in xrange(Margin,im.width -Margin): list = array.array('f') for y in xrange(ts2): for x in xrange(ts2): idx = ( x+tx-Margin + ( y+ty-Margin ) * wlen ) * Components list.append( buf[ idx ] ) tiles.append( np.frombuffer(list,dtype=np.float32) ) yield tiles yield []
# 144.34209299087524秒
単純に型を追加するだけでだいたいPythonの倍速くなる。すごい!
公式のチュートリアルによるとバッファーの型を指定することでもっと高速化することができるらしい。
cdef np.ndarray[float,ndim=1] buf
しかしこれをやるとコンパイルが通らなくなる。
Buffer types only allowed as function local variables
というエラーなのだが、ちゃんと関数内で定義しているのにさっぱり意味がわからない。
1日悩んで、どうやらyieldしているとこの定義ができないらしい。だから次のようにyieldが必要な部分とそうでない部分に分離する。
@cython.boundscheck(False) @cython.wraparound(False) def get_tiles2d(im,int ts2,int off): #cdef np.ndarray[float,ndim=1] buf cdef np.ndarray buf cdef int ty buf = np.array(im.getdata(),np.float32).reshape( im.width*im.height*Components ) / 255.0 for ty in xrange(Margin,im.height -Margin): yield get_tiles2_(im,ts2,off,ty,im.width,buf) yield [] @cython.boundscheck(False) # 関数全体で境界チェックを無効化 @cython.wraparound(False) cdef object get_tiles2_(im,int ts2,int off ,int ty,int wlen,np.ndarray[float,ndim=1] buf): cdef int x,y,tx cdef int idx list = array.array('f') for tx in xrange(Margin,im.width -Margin): for y in xrange(ts2): for x in xrange(ts2): idx = ( x+tx-Margin + ( y+ty-Margin ) * wlen ) * Components list.append( buf[ idx ] ) return np.frombuffer(list,dtype=np.float32).reshape( im.width-Margin*2,ts2*ts2)
# 95.15703916549683秒
キタコレ! オリジナルコードの3倍速い!
でもねぇ、C言語でばりばりに高速化したら3倍どころじゃないと思うんですよ。
もうちょっとなんとかならないもんですかねぇ。
arrayで馬鹿正直にappendするんじゃなくて、バッファーに直接書き込むようなことはできないんでしょうか。
いろいろ試してみたところ、次のようなコードでコンパイルが通った。
@cython.boundscheck(False) @cython.wraparound(False) cdef object get_tiles2_d(im,int ts2,int off ,int ty,int wlen,np.ndarray[float,ndim=1] buf): cdef int x,y,tx cdef int idx cdef np.ndarray[float,ndim=1] list = np.empty(ts2*ts2*(im.width-Margin*2), dtype=np.float32) cdef int ci = 0 cdef int yy for tx in xrange(Margin,im.width -Margin): for y in xrange(ts2): idx = ( tx-Margin + ( y+ty-Margin )*wlen ) * Components for x in xrange(ts2): list[ci] = buf[ idx ] ci += 1 idx += Components return list.reshape( im.width-Margin*2,ts2*ts2)
見るがいい!かものはしペリー!
# 5.6306798458099365秒
オリジナルコードの53倍速くなったぞ!
今の私にはこれ以上高速化できなそうなので、後はこの記事を見たプロフェッショナルに任せよう。
強い人工知能を作りたい
皆さんはICOTを知っているだろうか?
かつて日本が次世代のITリーダーとなるために第5世代コンピューター、人工知能を国家プロジェクトとして研究していた超かっちょいい計画だ。
私は子供の頃にICOTのことをテレビで見て以来、人工知能に強烈に憧れた。HAL9000のかっこよさにシビれた。HAL9000に近づくためにPrologも勉強した。
大人になってからは株を人工知能で予測できないかとひたすらデータの収集とニューラルネットワークの構築と調教にがんばってきた。ある程度の成果がでて、万を辞して超期待しながら本物の相場に挑み、1時間で100万円損して私の心はポッキリ折れた・・
しばらく人工知能から遠ざかっていたが、最近、GPUとディープラーニングのおかげで人工知能がアツイ。劇アツだ! 私は湧き上がる衝動を抑えきれないのを再び感じた!
GPUがアツイとか言いながら、とりあえずErlang + C++でニューラルネットワークの構築に挑んだわけだが、とにかく遅い。時間がかかる! やはりGPU、CUDAの勉強をしなければならないようだ。そんなころ、Python,Chainerとかいうキーワードが飛び込んできた。なんでもこれを使うと簡単にニューラルネットワークを構築できるとか!? そして試してみた。GPU速い! 速すぎる! 俺は今までのものはさっさと捨ててPython + Chainerで人工知能を始めることにした。目標は強い人工知能。シンギュラリティを起こしてあんなこともこんなこともうはうはするのが目的である。