Category Archives: cuda-convnet

(23) GTX760からGTX780に変えてみる

(22) cuda-convnet2はやってみれない (T_T) で残念な思いをしたのは20日ほど前のこと。
ここ最近で1万円近く値段が下がってきたGTX780を4万円少々で衝動買いした。

27cmもある長いボードは窮屈な筐体内で電気ストーブのように高熱を発しており…
しばらくはこまめに温度を確認しながら使うことになりそう。20140911_01

中身をのぞいてみる

deviceQueryの結果は以下の通り。
Compute capability 3.5 がうれしい!
GTX780のcore数は2304個、GTX650 384個の6倍、GTX760 1152個の2倍になった!

Device 0: "GeForce GTX 780"
  CUDA Driver Version / Runtime Version          6.0 / 6.0
  CUDA Capability Major/Minor version number:    3.5
  Total amount of global memory:                 3071 MBytes (3220504576 bytes)
  (12) Multiprocessors, (192) CUDA Cores/MP:     2304 CUDA Cores
  GPU Clock rate:                                1058 MHz (1.06 GHz)
  Memory Clock rate:                             3104 Mhz
  Memory Bus Width:                              384-bit
  L2 Cache Size:                                 1572864 bytes

cuda-convnetで速度計測

(21) GTX650からGTX760に変えてみる のときと同じく、ちょっとリッチなMNIST学習用ネットワーク100epochsで計測した。

結果1: 最新環境(intel Xeon x5570 x 2, GTX780)
100epochsの処理時間は 100秒 、エラー率は 0.010だった。

START: 2014年  9月 11日 木曜日 20:57:46 JST
======================Test output======================
logprob:  0.034160, 0.010400
END:   2014年  9月 11日 木曜日 20:59:26 JST

結果2: 直前環境(intel Xeon x5570 x 2, GTX760)
100epochsの処理時間は 157秒 、エラー率は 0.010だった。

START: 2014年  8月 18日 月曜日 15:24:10 JST
======================Test output======================
logprob:  0.034883, 0.010300
END:   2014年  8月 18日 月曜日 15:26:47 JST

結果3: 旧マシン(intel core-i3 3220, GTX650)
100epochsの処理時間は 398秒 、エラー率は 0.011だった。

START: 2014年  8月 18日 月曜日 15:23:48 JST
======================Test output======================
logprob:  0.034047, 0.010900
END:   2014年  8月 18日 月曜日 15:30:26 JST

(22) cuda-convnet2はやってみれない (T_T)

2014年7月17日、cuda-convnet の作者が cuda-convnet2 なるプログラムをGIT上に公開した。
現在も開発が進行中で、その様子はこちら(↓)で眺められる。
https://github.com/akrizhevsky/cuda-convnet2
20140822_01

これはぜひやってみよう!

と意気揚々とプログラムをダウンロードし、
  必要なライブラリをインストールし、
    ビルドが成功し、

いざ実行!

=========================
Running on CUDA device(s) 0
Current time: Fri Aug 22 21:51:07 2014
Saving checkpoints to /home/user/cuda/cuda-convnet2/save/cifar-10/ConvNet__2014-08-22_21.51.06
=========================
src/nvmatrix.cu(394) : getLastCudaError() CUDA error : kSetupCurand: Kernel execution failed : (8) invalid device function .

エラーで止まった (TT)

いろいろ調べると、コンパイル時に指定した Compte Capability version が、
マシンに搭載している GPUと合っていないことが原因のようだ。

そこで…
うちの新しい GTX-760 の Compute Capabilityを調べてみる。
これには NVIDIA_CUDA-6.0_Samples に付属の deviceQuery コマンドが使える。

[user@]$ ./deviceQuery
./deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GTX 760"
  CUDA Driver Version / Runtime Version          6.0 / 6.0
  CUDA Capability Major/Minor version number:    3.0
  Total amount of global memory:                 2047 MBytes (2146762752 bytes)
  ( 6) Multiprocessors, (192) CUDA Cores/MP:     1152 CUDA Cores
  GPU Clock rate:                                1058 MHz (1.06 GHz)
  Memory Clock rate:                             3004 Mhz
  Memory Bus Width:                              256-bit
  L2 Cache Size:                                 524288 bytes
 :

うちのは Compute Capability version 3.0 なのだそうだ。

では、cuda-convnet2 を Compute Capability ver.3.0 指定でビルドしてみる。
MakeFile中で以下のように書かれているところを探し、すべて3.0指定に書き換えた。

GENCODE_SM35    := -gencode arch=compute_35,code=sm_35
GENCODE_FLAGS   := $(GENCODE_SM35)

再度ビルドを実行する。

[user@]$ ./build.sh clean
[user@]$ ./build.sh

今度こそは!
と cuda-convnet2 で Cifar-10 自動認識を起動!

すると…

さっきよりもヤバゲなエラーが出た (T^T)

=========================
Running on CUDA device(s) 0
Current time: Fri Aug 22 22:00:59 2014
Saving checkpoints to /home/user/cuda/cuda-convnet2/save/cifar-10/ConvNet__2014-08-22_22.00.58
=========================
1.1 (0.00%)...python: src/nvmatrix.cu:1473: virtual cudaTextureObject_t NVMatrix::getTextureObject(): Assertion `_texObj != 0' failed.
Error signal 6:
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_Z13signalHandleri+0x26)[0x7fb5b9f2a3e6]
/lib64/libc.so.6[0x35e22329a0]
/lib64/libc.so.6(gsignal+0x35)[0x35e2232925]
/lib64/libc.so.6(abort+0x175)[0x35e2234105]
/lib64/libc.so.6[0x35e222ba4e]
/lib64/libc.so.6(__assert_perror_fail+0x0)[0x35e222bb10]
./nvmatrix/libnvmatrix.so(_ZN8NVMatrix16getTextureObjectEv+0x147)[0x7fb5b8c15f67]
./cudaconv3/libcudaconv.so(_Z11_filterActsR8NVMatrixS0_S0_iiiiiiiffb+0x5c25)[0x7fb5b7f984e5]
./cudaconv3/libcudaconv.so(_Z14convFilterActsR8NVMatrixS0_S0_iiiiiiiff+0x30)[0x7fb5b7f9dd30]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN9ConvLayer9fpropActsEifji+0x18e)[0x7fb5b9ee5d8e]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN5Layer5fpropERSt3mapIiP8NVMatrixSt4lessIiESaISt4pairIKiS2_EEEji+0x240)[0x7fb5b9ef2ab0]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN5Layer5fpropEji+0x23f)[0x7fb5b9ef2d4f]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN13ConvNetThread3runEv+0x17d)[0x7fb5b9f44d6d]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN6Thread18start_pthread_funcEPv+0x9)[0x7fb5b9ef7b69]
/lib64/libpthread.so.0[0x35e26079d1]
/lib64/libc.so.6(clone+0x6d)[0x35e22e8b5d]
CUDA error at src/nvmatrix.cu:522 code=29(cudaErrorCudartUnloading) "cudaSetDevice(d)"
Error signal 11:
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_Z13signalHandleri+0x26)[0x7fb5b9f2a3e6]
/lib64/libc.so.6[0x35e22329a0]
/lib64/libc.so.6(exit+0x35)[0x35e2235d75]
./nvmatrix/libnvmatrix.so(+0x30886)[0x7fb5b8c15886]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN14DataCopyThread3runEv+0x4ac)[0x7fb5b9ee3b9c]
/home/user/cuda/cuda-convnet2/cuda-convnet2/cudaconvnet/_ConvNet.so(_ZN6Thread18start_pthread_funcEPv+0x9)[0x7fb5b9ef7b69]
/lib64/libpthread.so.0[0x35e26079d1]
/lib64/libc.so.6(clone+0x6d)[0x35e22e8b5d]

エラーの内容を見ると、どうやらうちのGTX760では未サポートの機能を使おうとしている様子…

そこで cuda-convnet2 のホームページを改めて見てみると…
20140822_02

Compute Capability 3.5以上じゃないとダメ

と書いてあった…

2014年8月現在、GTX780を手に入れるには 6万円前後のお金が必要だ…
先日Xeon x5570 x2 マシンを 6万円で購入したので、手元にお金は残っていない…

cuda-convnet2 はヤフオクで GTX780が2万円台に値下がりするまでしばらくあきらめよう(T^T)


参考情報

CUDA ZONEのページに GPU型番別の Compute Capavility Version一覧が載っている。
https://developer.nvidia.com/cuda-gpus20140822_03

(17) cuda-convnetで自筆数字画像を自動認識

試してみたいこと

(6) 自分の手書き文字を認識させてみる では、自作の octave版シンプル構成ニューラルネットに自筆の数字画像を自動認識させてみた。下記のように、どれも意地悪でない素直な数字画像を描いたつもりだが、MNISTテスト画像で 90% 以上の正解率を出したニューラルネット上での自筆画像の正解率は 65% (13/20) であった…


OK

OK

OK

OK

OK

OK

OK

OK

NOK

OK

OK

OK

NOK

NOK

NOK

OK

OK

NOK

NOK

NOK

チープな自作ニューラルネットに対して (15) cuda-convnetでMNIST自動認識(その2) では、cuda-convnet の強力パワーを利用してリッチなネットワーク構成で学習した結果、正解率 98% のニューラルネットが出来上がった。今回は、上記の残念な結果になってしまった自筆画像を、このニューラルネットに自動認識させてみたい。

入力データを作らなければ

自筆の数字画像はただのPNG画像ファイルのため、これを cuda-convnet に入力できる形式に変換しなければならない。実現方法は単純だ。

(1) PNG形式の自筆数字画像ファイルを、MNIST生データ形式(=MNISTサイトからダウンロードしてきたときのファイル形式)に変換する。
(2) 作成したMNIST生データ形式ファイルを (13) cuda-convnet用MNISTデータを作る(その3) で作成した data_batch_n 作成プログラムで cuda-convnet 入力ファイル形式に変換する。
(3) cuda-convnetを test-onlyで実行させる。

入力データは以下のプログラムで作成した。
拙いPythonスキルで試行錯誤しながら書いたので無駄がいっぱいあるかも…

import numpy as np
import Image
import struct
import glob

data = ''
labl = ''
data += struct.pack('>4i', 0, 20, 28, 28)   # dummy header
labl += struct.pack('>2i', 0, 20)

files = glob.glob('./*.png')
files.sort()
for file in files:
    #------------------------
    # (1)make label data
    #------------------------
    lbl = file.split('_')
    lbl = int(lbl[1])
    labl += struct.pack('B', lbl)
    #------------------------
    # (2)make image data
    #------------------------
    img = Image.open(file)
    d = np.array(img)
    d = np.delete(d, 3, 2)   # index=3, axis=2 : RGBA -> RGB
    d = np.mean(d, 2)        # RGB -> L
    d = np.swapaxes(d, 0, 1)
    d = np.uint8(d.T.flatten('C'))    # [y][x] -> [y*x]
    ld = d.tolist()          # numpy object -> python list object
    ad = struct.pack('784B', *ld)
    data += ad
fp = open('my_MNIST_label','w')
fp.write(labl)
fp.close()
fp = open('my_MNIST_data','w')
fp.write(data)
fp.close()
# make data_batch_n
import makeDataBatch
makeDataBatch.make_data_batch_all('','','my_MNIST_data','my_MNIST_label',10000)

cuda-convnetで自動認識実行(学習10epoch)

きれいにならべて結果表示できるように shownet.py に少々手を加えた後に実行してみた。

[user@linux]$ SAVEDATA=../save/MNIST/ConvNet__2014-07-09_22.10.42/
[user@linux]$ python shownet.py -f $SAVEDATA --show-preds=probs --test-range=8

結果は…
20140712_01
15/20で正解率 75%
数字の 46 が全滅…
各数字10,000枚も学習していて、テストでは 98% も正解しているのに…
自分が描いた4と6はそんなに世の中の普通からずれているのか?

ちょっと気になるので

「本当に自分の 46 は汚いのか?」
「もともと 46 は自動認識が難しいのではないか?」
と自分を納得させたく、(16) cuda-convnetの結果からerror matrixを作成 の手順で、正解率 98% のテスト結果の error matrix を作成し、眺めてみたくなった。

結果は…
20140712_02
49 と間違えることが多いようだ。
65 と間違えることが多いようだ。

確かに自分の描いた 65 と誤認識されているが、931/958 に入れず 12/958 に入ってしまうレアな形の字を 2/2の確率で描けたことがすごいのでは…

それでも…
error matrixを眺めてみると、特別 46 に弱いわけではなさそうだ。
ということは、自分の字が汚いということか…
ん?98.0%が 97.8%になっているのはなぜ?

cuda-convnetで自動認識実行(学習10,000epoch)

さらにEPOCH数を10,000回に設定し、12時間かけて学習させてみたら以下のような結果になった。
MNISTトレーニングデータに関しては 99.9% の正解率、MNISTテストデータは 98.8% だ!

======================================================= (1.254 sec)
10000.1... logprob:  0.008284, 0.000900 (0.623 sec)
10000.2... logprob:  0.007593, 0.000700 (0.622 sec)
10000.3... logprob:  0.007735, 0.000600 (0.622 sec)
10000.4... logprob:  0.008328, 0.000500 (0.622 sec)
10000.5... logprob:  0.007779, 0.000100 (0.622 sec)
10000.6... logprob:  0.007282, 0.000200 
======================Test output======================
logprob:  0.034618, 0.011600 

この学習結果を使用して自筆テストデータを自動認識させてみた結果は、正解数 18/20, 正解率 90% と向上した。
20140712_03

ちなみにこのときのMNISTテストデータの error matrix は以下の通り。
20140712_04

(16) cuda-convnetの結果からerror matrixを作成

(1)全テスト結果を取得する。

cuda-convnet のテスト結果出力機能を使うと、各テスト画像について出力層の全ユニットの出力値が取得できる。
例えば、学習結果 ConvNet__2014-07-09_22.31.05 が存在している場合、以下のように取得できる。

[user@linux]$ python shownet.py -f ./save/ConvNet__2014-07-09_22.31.05 --write-features=probs --feature-path=../tmp/

実行した結果、–feature-path で指定したディレクトリにテスト結果が出力される。

[user@linux]$ ll
合計 436
-rw-rw-r--. 1 user user     78  7月  9 22:54 2014 batches.meta
-rw-rw-r--. 1 user user 440191  7月  9 22:54 2014 data_batch_6

まずは出力された batches.meta の中身を見てみる。

>>> import cPickle as cp
>>> mt = cp.load(open('batches.meta'))
>>> mt.keys()
['num_vis', 'source_model']
>>> mt['num_vis']
10

num_vis からラベル数が取得できるようだ。

次に data_batch_6 の中身を見てみる。

>>> sv = cp.load(open('data_batch_6'))
>>> sv.keys()
['labels', 'data']
>>> sv['labels']
array([[ 3.,  8.,  8., ...,  5.,  1.,  7.]], dtype=float32)
>>> sv['data']
array([[  2.12459755e-03,   2.84853554e-03,   8.47100373e-03, ...,
          1.67425780e-03,   6.06818078e-03,   1.81574584e-03],
       [  9.24929883e-03,   9.61963892e-01,   7.97947752e-04, ...,
          7.32670560e-06,   2.74835732e-02,   3.86457832e-04],
       ...,
       [  6.13748282e-02,   3.04950565e-01,   1.83501482e-01, ...,
          1.76601484e-02,   1.54394070e-02,   5.21140127e-03],
       [  7.03860400e-03,   1.66299902e-02,   1.75639763e-02, ...,
          6.83449626e-01,   1.96132925e-03,   3.77259240e-03]], dtype=float32)

labels には正解ラベル、data には各画像の出力層全ユニット出力値が書かれている。

(2)error matrixを作成する。

上記(1)で作成したデータから error matrix を作成する関数は以下の通り。
拙いPythonスキルで試行錯誤しながら書いたので無駄がいっぱいあるかも…

import cPickle as cp
import numpy as np

def makeErrorMatrix( metaFile, dataFile ):
    mt = cp.load(open(metaFile))
    sv = cp.load(open(dataFile))
    nLabel = mt['num_vis']
    nImage = sv['labels'].shape[1]

    print '# of label : %d' % nLabel
    print '# of image : %d' % nImage

    maxAryRaw = np.uint8(np.argmax(sv['data'], 1))
    maxArySpv = np.uint8(sv['labels'].T.flatten())

    errorMatrix = np.zeros((nLabel,nLabel),dtype=int)
    for i in range(0,nImage):
        x = maxAryRaw[i]
        y = maxArySpv[i]
        errorMatrix[y][x] = errorMatrix[y][x] + 1
    np.savetxt('errmtx.csv', errorMatrix, fmt='%d', delimiter=',')

CIFAR-10 のテスト結果から作成したCSVファイルはこれ(↓)

738, 42,120, 13, 13,  6,  5, 10, 28, 25
 28,842, 21, 14, 11,  7,  2,  2,  5, 68
 68, 20,599, 56, 95, 99, 33, 16,  6,  8
 39, 31,114,466, 66,193, 48, 24,  7, 12
 44, 12,126, 55,572, 83, 32, 64,  4,  8
 25, 11, 87,214, 38,571, 11, 30,  4,  9
  7, 15,103, 85, 92, 53,633,  3,  5,  4
 18,  8, 67, 63, 72,102,  3,644,  0, 23
145, 91, 62, 27,  7,  9,  3,  3,627, 26
 54,190, 11, 12,  7, 11,  5, 19, 14,677

MS-Excelで整形すると、それっぽい error matrix が出来上がる。
今回の分類結果を見ると…
1) recallを見るとautomobileの正解率が突出して高いように見えるが、precisionを見ると正確さは平均並み。
  → 全テストデータ中でautomobileと認識される数は多いが、実際にautomobileである確率は並
2) recallを見るとshipの正解率は平均並みだが、precisionを見ると正確さは突出している。
  → shipと認識されたときに、本当にshipである確率が高い。
3) truckをautomobileとよく間違える。
  → 写真だけ見たら確かに似てるか…
4) catとdogはお互いによく間違える。
  → これもシルエットだけ見たら区別がつかないかも…
などの特徴が見られて面白い。
20140709_05

(15) cuda-convnetでMNIST自動認識(その2)

(14) cuda-convnetでMNIST自動認識 に続き、今度はちょっとリッチなレイヤー構成で試してみる。

ちょっとリッチなレイヤー構成はこんな感じ。
[IN]-[CONV][POOL][CONV][POOL]-[OUT]の結合で、[CONV] [POOL] は各16枚の厚みを持たせている。

[data]
type=data
dataIdx=0

[labels]
type=data
dataIdx=1

[conv1]
type=conv
inputs=data
channels=1
filters=16
padding=0
stride=1
filterSize=5
initW=0.0001
partialSum=1
sharedBiases=1
neuron=tanh[1,1]

[pool1]
type=pool
pool=max
inputs=conv1
start=0
sizeX=2
stride=2
outputsX=0
channels=16

[conv2]
type=conv
inputs=pool1
filters=16
padding=0
stride=1
filterSize=5
channels=16
neuron=tanh[1,1]
initW=0.01
partialSum=1
sharedBiases=1

[pool2]
type=pool
pool=avg
inputs=conv2
start=0
sizeX=2
stride=2
outputsX=0
channels=16

[fcOut]
type=fc
outputs=10
inputs=pool2
initW=0.01
initB=0.1

[probs]
type=softmax
inputs=fcOut

[logprob]
type=cost.logreg
inputs=labels,probs

学習率、momentum、L2正則化係数はこんな感じ。

[conv1]
epsW=0.01
epsB=0.01
momW=0.9
momB=0.9
wc=0.0001

[conv2]
epsW=0.01
epsB=0.01
momW=0.9
momB=0.9
wc=0.0001

[fcOut]
epsW=0.01
epsB=0.01
momW=0.9
momB=0.9
wc=0.0001

[logprob]
coeff=1

実行結果

1epoch完了時点で正解率が 93.3% (error rate=0.0670)
10epoch完了時点で正解率が 98.0% (error rate=0.0194)

=========================
1.1... logprob:  1.066792, 0.262400 (1.194 sec)
1.2... logprob:  0.411331, 0.117200 (0.622 sec)
1.3... logprob:  0.342615, 0.099900 (0.623 sec)
1.4... logprob:  0.316771, 0.091300 (0.623 sec)
1.5... logprob:  0.282887, 0.084500 (0.623 sec)
1.6... logprob:  0.230386, 0.063700 
======================Test output======================
logprob:  0.221841, 0.067000 
---
10.1... logprob:  0.073848, 0.020500 (0.623 sec)
10.2... logprob:  0.074025, 0.020300 (0.623 sec)
10.3... logprob:  0.074177, 0.020200 (0.622 sec)
10.4... logprob:  0.077257, 0.023000 (0.623 sec)
10.5... logprob:  0.077007, 0.024400 (0.622 sec)
10.6... logprob:  0.063696, 0.017700 
======================Test output======================
logprob:  0.068057, 0.019400 

実際の自動認識結果を見てみると…

[user@linux]$ SAVEDATA=../save/MNIST/ConvNet__2014-07-09_22.10.42/
[user@linux]$ python ../cuda-convnet-read-only/shownet.py -f $SAVEDATA --show-preds=probs

20140709_03
20140709_04

既存の学習結果を使用してテストだけ実行したいときは…

convnet.pyの引数は下記の3個だけでよい。

python convnet.py -f xxxx/ConvNet__2014-07-08_00.22.30 --test-only=1 --test-range=6

(14) cuda-convnetでMNIST自動認識(その1)

(12) cuda-convnet用MNISTデータを作る(その2)batches.meta を作り、
(13) cuda-convnet用MNISTデータを作る(その3)data_batch_n を作り、
いよいよ cuda-convnetMNIST を学習&テストしてみる。

まずは (4) シンプル構成の初版は正解率91% で最高スコアを記録した
「4層 [784]-[256]-[64]-[10] の正解率91.9%」
に近いレイヤー構成で試してみる。

レイヤーの構成はこんな感じ。

[data]
type=data
dataIdx=0

[labels]
type=data
dataIdx=1

[fc1]
type=fc
inputs=data
outputs=256
initW=0.1
neuron=logistic

[fc2]
type=fc
inputs=fc1
outputs=64
initW=0.1
neuron=logistic

[fcOut]
type=fc
outputs=10
inputs=fc2
initW=0.1
initB=0.1

[probs]
type=softmax
inputs=fcOut

[logprob]
type=cost.logreg
inputs=labels,probs

学習率、momentum、L2正則化係数はこんな感じ。

[fc1]
epsW=0.05
epsB=0.05
momW=0.9
momB=0.9
wc=0.00001

[fc2]
epsW=0.05
epsB=0.05
momW=0.9
momB=0.9
wc=0.00001

[fcOut]
epsW=0.05
epsB=0.05
momW=0.9
momB=0.9
wc=0.00001

[logprob]
coeff=1

実行結果!

1epoch完了時点で正解率が 88.5% (error rate=0.1146)
10epoch完了時点で正解率が 93.6% (error rate=0.0640)

=========================
1.1... logprob:  1.470148, 0.409900 (0.660 sec)
1.2... logprob:  0.599354, 0.162500 (0.102 sec)
1.3... logprob:  0.474667, 0.133800 (0.102 sec)
1.4... logprob:  0.441993, 0.130400 (0.102 sec)
1.5... logprob:  0.427795, 0.127700 (0.102 sec)
1.6... logprob:  0.363772, 0.107600 
======================Test output======================
logprob:  0.382512, 0.114600 
---
======================================================= (0.150 sec)
10.1... logprob:  0.215052, 0.064400 (0.103 sec)
10.2... logprob:  0.224370, 0.068200 (0.102 sec)
10.3... logprob:  0.226908, 0.070900 (0.102 sec)
10.4... logprob:  0.212489, 0.065500 (0.102 sec)
10.5... logprob:  0.231503, 0.069500 (0.102 sec)
10.6... logprob:  0.192322, 0.057600 
======================Test output======================
logprob:  0.209273, 0.064000 

実際の自動認識結果を見てみると…

[user@linux]$ SAVEDATA=../save/MNIST/ConvNet__2014-07-09_21.45.35/
[user@linux]$ python ../cuda-convnet-read-only/shownet.py -f $SAVEDATA --show-preds=probs

20140709_01
20140709_02

(13) cuda-convnet用MNISTデータを作る(その3)

(12) cuda-convnet用MNISTデータを作る(その2) ではMNIST学習データファイルから cuda-convnet入力用の学習データ情報ファイル batches.meta を作成してみた。
今回は学習データ、テストデータそのものを格納する data_batch_n を作成してみる。

cifar10の data_batche_1 の中身はこんな感じ。

>>> import cPickle as cp
>>> d = cp.load(open('data_batch_1'))
>>> d.keys()
['batch_label', 'labels', 'data', 'filenames']
>>> d['batch_label']
'training batch 1 of 5'
>>> d['labels']
[6, 9, 9, 4, 1, 1, 2, 7, 8, 3, 4, 
 ...,
 9, 2, 2, 1, 6, 3, 9, 1, 1, 5]
>>> d['data']
array([[ 59, 154, 255, ...,  71, 250,  62],
       [ 43, 126, 253, ...,  60, 254,  61],
       ...,
       [ 84, 142,  83, ...,  69, 255, 130],
       [ 72, 144,  84, ...,  68, 254, 131]], dtype=uint8)
>>> d['filenames']
['leptodactylus_pentadactylus_s_000004.png', 'camion_s_000148.png', 'tipper_truck_s_001250.png', 'american_elk_s_001521.png',
 ...,
 , 'car_s_002296.png', 'estate_car_s_001433.png', 'cur_s_000170.png']

data_batche_n の中に格納されているデータは4種類ある。
1) batch_label : データファイルの説明
2) labels : 正解ラベル
3) data : 画像データ
4) filenames : 元の画像ファイル名

作成した data_batche_n作成関数はこちら。
拙いPythonスキルで試行錯誤しながら書いたので無駄がいっぱいあるかも…

import cPickle as cp
import numpy as np
import Image
import struct

def make_data_batch_all( train_img, train_lbl, test_img, test_lbl, nDataPerBatch ):
    iBatch = 1
    iBatch = make_data_batch( train_img, train_lbl, nDataPerBatch, iBatch, 'training' )
    iBatch = make_data_batch( test_img,  test_lbl,  nDataPerBatch, iBatch, 'testing' )

def make_data_batch( img, lbl, nDataPerBatch, iBatch, batchLabel ):
    fpImg = open( img, 'rb' )
    fpLbl = open( lbl, 'rb' )
    headerImg = fpImg.read( 4 * 4 )
    headerLbl = fpLbl.read( 4 * 2 )

    headerImg_up = struct.unpack('>4i', headerImg)
    headerLbl_up = struct.unpack('>2i', headerLbl)

    nImg  = headerImg_up[1]
    img_w = headerImg_up[2]
    img_h = headerImg_up[3]
    nPixelsOf1img = img_w * img_h

    print 'img : # of image             : %d' % nImg
    print 'img : image width            : %d' % img_w
    print 'img : image height           : %d' % img_h
    print 'img : # of pixels in a image : %d' % nPixelsOf1img

    nBatchMax = np.ceil(np.float32(nImg) / np.float32(nDataPerBatch))
    fmtImg  = '%dB' % nPixelsOf1img
    save_data = []
    save_filenames = []
    save_labels = []
    nOutData = 0

    for iImg in range(0,nImg):
    #for iImg in range(0,1):
        if (iImg % 1000 == 0) or (iImg == nImg-1):
            print 'now processing image #%d' % iImg
        # append data
        data = fpImg.read( nPixelsOf1img )
        data = struct.unpack(fmtImg, data)
        data = np.asarray( data ).astype('uint8')
        save_data.append(data.T.flatten('C'))
        nOutData = nOutData + 1
        # append filenames
        save_filenames.append('')
        # append labels
        data = fpLbl.read(1)
        data = struct.unpack('B', data)
        #save_labels.append((list(data))[0])
        save_labels.append(data[0])
        # save data_batch_n file
        if ((iImg > 0) and (iImg % nDataPerBatch == nDataPerBatch-1)) or (iImg == nImg-1):
           fname = 'data_batch_%d' % iBatch
           fpOut = open( fname, 'w+' )
           label = '%s batch %d of %d' % (batchLabel, iBatch, nBatchMax)
           save_data = np.reshape(save_data, (nOutData, img_w, img_h))
           save_data = np.uint8(save_data)
           save_data = save_data.swapaxes(0,2)
           #save_data = save_data.swapaxes(0,1)
           save_data = np.reshape(save_data, (img_w * img_h, nOutData))
           #save_labels = save_labels.T.flatten('C')
           dic = {'batch_label':label, 'data':save_data, 'labels':save_labels, 'filenames':save_filenames}
           cp.dump( dic, fpOut )
           fpOut.close()
           iBatch = iBatch + 1
           # clear data
           save_data = []
           save_filenames = []
           save_labels = []
           nOutData = 0
    fpImg.close()
    fpLbl.close()
    return iBatch

作成した data_batch_n を確認してみる。
元画像ファイル名は存在しないので空文字としている。

>>> import cPickle as cp
>>> d = cp.load(open('data_batch_1'))
>>> d.keys()
['batch_label', 'labels', 'data', 'filenames']
>>> d['batch_label']
'training batch 1 of 6'
>>> d['labels']
[5, 0, 4, 1, 9, 2, 1, 3, 1, 4, 3, 5,
 ...,
 7, 2, 7, 3, 7, 4, 0, 5, 8, 6, 9, 7]
>>> d['data']
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)
>>> d['filenames']
['', '', '', '', '', '', '', '', '',
 '', '', '', '', '', '', '', '', '']

OKそうだ!
次回は作成済みの batches.meta data_batch_n を使用してcuda-convnet を動かしてみる予定。

(12) cuda-convnet用MNISTデータを作る(その2)

(11) cuda-convnet用MNISTデータを作る(その1) ではMNISTデータファイルを pythonでロードする手順を確認した。今回はロードしたデータから cuda-convnet用 batches.meta ファイルを作成してみる。

cifar10のbatches.meta の中身はこんな感じ。

>>> import cPickle as cp
>>> meta = cp.load(open('batches.meta'))
>>> meta.keys()
['num_cases_per_batch', 'label_names', 'num_vis', 'data_mean']
>>> meta['num_cases_per_batch']
10000
>>> meta['label_names']
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
>>> meta['num_vis']
3072
>>> meta['data_mean']
array([[ 134.45458984],
       [ 134.22865295],
       [ 134.84976196],
       ...,
       [ 115.09417725],
       [ 115.32595062],
       [ 115.7903595 ]], dtype=float32)
>>> meta['data_mean'].size
3072
>>> im['data_mean'].ndim
2

batches.metaの中に格納されているデータは4種類ある。
1) num_cases_per_batch : 1バッチの画像データ数
2) label_names : ラベル文字列のリスト
3) num_vis : 1画像のデータ数(=ピクセル数×チャネル数)
4) data_mean : 全画像のピクセル×チャネルごとの平均値

作成した batches.meta作成関数はこちら。
拙いPythonスキルで試行錯誤しながら書いたので無駄がいっぱいあるかも…

def make_batches_meta( trainDataFilePath, nDataPerBatch ):
    infile = open( trainDataFilePath, 'rb' )
    header = infile.read( 4 * 4 )
    header_up = struct.unpack('>4i', header)   # > : big endian, 4i: 4 x int(32bit)
    nImg  = header_up[1]
    img_w = header_up[2]
    img_h = header_up[3]
    nPixelsOf1img = img_w * img_h

    print '# of image             : %d' % nImg
    print 'image width            : %d' % img_w
    print 'image height           : %d' % img_h
    print '# of pixels in a image : %d' % nPixelsOf1img

    # calculate average
    meanData = np.zeros(nPixelsOf1img, dtype=np.float32)
    for i in range(0,nImg):
        data = infile.read( nPixelsOf1img )
        fmt  = '%dB' % nPixelsOf1img
        data_up = struct.unpack(fmt, data)
        meanData = meanData + data_up
    meanData = meanData / nImg
    meanData = np.float32(meanData.reshape((nPixelsOf1img,1)))
    infile.close()

    # save batches.meta
    label_names = ['0','1','2','3','4','5','6','7','8','9']
    num_vis     = img_w * img_h
    meanData = np.reshape(meanData, (img_w, img_h))
    #meanData = meanData.swapaxes(0,1)
    meanData = meanData.T.flatten('C')
    meanData = np.reshape(meanData,(nPixelsOf1img,1))
    dic = {'num_cases_per_batch':nDataPerBatch, 'label_names':label_names, 'num_vis':num_vis, 'data_mean':meanData}
    fp = open('./batches.meta','w+')
    cp.dump( dic, fp )
    fp.close()

作成した batches.meta を確認してみる。

[user@linux]$ python
>>> import cPickle as cp
>>> im = cp.load(open('batches.meta'))
>>> im
{'num_cases_per_batch': 10000, 'label_names': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'num_vis': 784, 'data_mean': array([[  0.00000000e+00],
       [  0.00000000e+00],
       [  0.00000000e+00],
       ...,
       [  0.00000000e+00],
       [  0.00000000e+00],
       [  0.00000000e+00]], dtype=float32)}

OKそうだ!
次回は data_batch_n を作成してみる予定です。

(11) cuda-convnet用MNISTデータを作る(その1)

(1) MNIST画像データをダウンロード でダウンロードしたMNISTデータを、cuda-convnetプログラムで入力可能な形式に変換したい。Pythonに不慣れなこともあり、まずはファイルから入力した画像データを表示し、正しく読めていることを確認してみる。

事前にPythonでの画像表示に必要な以下のプログラムをインストールしておく必要がある。
PIL(Python Imaging Library)
ImageMagick

(1) 作業ディレクトリにはMNISTデータファイルが置いてある。

[user@linux]$ ll
-rw-rw-r--. 1 user user  7840016  7\u6708  7 18:08 2014 t10k-images-idx3-ubyte
-rw-rw-r--. 1 user user    10008  7\u6708  7 18:08 2014 t10k-labels-idx1-ubyte
-rw-rw-r--. 1 user user 47040016  7\u6708  7 18:08 2014 train-images-idx3-ubyte
-rw-rw-r--. 1 user user    60008  7\u6708  7 18:08 2014 train-labels-idx1-ubyte

(2) Pythonを起動する。

[user@linux]$ python
Python 2.6.6 (r266:84292, Jan 22 2014, 09:42:36) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

(3) 使用するモジュールをロードする。

>>> import Image
>>> import numpy as np
>>> import struct

(4) train用データファイルをロードする。

>>> infile = open('./train-images-idx3-ubyte','rb')

(5) ヘッダ部のデータをロードする。
データファイルのフォーマットは (1) MNIST画像データをダウンロード を参照のこと。

>>> header = infile.read( 4 * 4 )
>>> header_up = struct.unpack('>4i', header)   # > : big endian, 4i: 4 x int(32bit) 
>>> numOfPixelsIn1Data = header_up[2] * header_up[3]
>>> print '# of image            : %d' % header_up[1]
# of image            : 60000
>>> print 'image width           : %d' % header_up[2]
image width           : 28
>>> print 'image height          : %d' % header_up[3]
image height          : 28
>>> print '# of pixels in a data : %d' % numOfPixelsIn1Data
# of pixels in a data : 784

(6) 先頭から5個の画像データを読み込み、表示する。

>>> for i in range(0,5):
...     data = infile.read( numOfPixelsIn1Data )
...     fmt  = '%dB' % numOfPixelsIn1Data
...     data_up = struct.unpack(fmt, data)
...     npData = np.asarray( data_up ).astype('uint8')
...     imData = np.reshape(npData, (28,28),order='C')
...     im = Image.fromarray( imData )
...     im.show()
... 

trainデータファイルの先頭にはこんな手書き数字画像が入っているようだ。
20140707_01

(7) 最後は(4)でオープンしたファイルをクローズする。

>>> infile.close()

次回はロードしたMNISTデータから cuda-convnetの batches.metaファイルを作成してみる予定です。

(10) cuda-convnetのレポート表示

(9) cuda-convnetを試してみる のトレーニング結果をレポート表示させてみます。
これも cuda-convnet が提供している機能です。

(1) cost functionの出力値

[user@linux]$ SAVEDATA=../save/ConvNet__2014-07-02_23.29.41
[user@linux]$ python shownet.py -f $SAVEDATA --show-cost=logprob

20140702_05

(2) error rate

[user@linux]$ python shownet.py -f $SAVEDATA --show-cost=logprob --cost-idx=1

20140702_06

(3) learned filters (conv1 layer)

conv1 とは、ネットワーク定義ファイルの中で当該レイヤーに付けた名前です。
先の実行時に指定したネットワーク定義ファイルはこれです。
–layer-def=./example-layers/layers-19pct.cfg

[user@linux]$ python shownet.py -f $SAVEDATA --show-filters=conv1

20140702_07

(4) learned filters (conv2 layer)

[user@linux]$ python shownet.py -f $SAVEDATA --show-filters=conv2

20140702_08

(5) learned filters (conv3 layer)

[user@linux]$ python shownet.py -f $SAVEDATA --show-filters=conv3

20140702_09

(6) learned filters (fc10 layer)

–channels=64 は、直前レイヤー(pool3)の channel数を指定します。

[user@linux]$ python shownet.py -f $SAVEDATA --show-filters=fc10 --channels=64

20140702_10

(7) 分類結果のうち8個をランダム表示

[user@linux]$ python shownet.py -f $SAVEDATA --show-preds=probs

正解したものは赤い線不正解のものは青い線 で表示されます。
線の長さは出力ユニットの出力値のはず…
20140702_11

ランダムなので毎回表示される画像が変わります。
20140702_12

(8) 分類結果のうち8個をランダム表示(エラーのみ)

–show-preds に加えて –only-errors=1 を指定すると、誤認識した結果のみを表示できます。

[user@linux]$ python shownet.py -f $SAVEDATA --show-preds=probs --only-errors=1

20140702_13