そのうちシンギュラリティを起こすblog

強い人工知能を作ってそのうちシンギュラリティを起こします。

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で人工知能を始めることにした。目標は強い人工知能。シンギュラリティを起こしてあんなこともこんなこともうはうはするのが目的である。